archgraph-argo 0.10.8 → 0.10.10
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 +14 -0
- package/argo/scripts/neo4j-system-architecture-store.js +27 -1
- package/cordis.patch.yml +6 -0
- package/dsh-argo-wakeup/index.js +17 -0
- package/dsh-argo-wakeup/package.json +1 -0
- package/dsh-argo-workspace/index.js +141 -0
- package/dsh-argo-workspace/package.json +1 -0
- package/install-argo.ps1 +1 -1
- package/package.json +20 -3
package/README.md
CHANGED
|
@@ -19,6 +19,20 @@ engineering:
|
|
|
19
19
|
|
|
20
20
|
Editable source: [`docs/diagrams/global-architecture.excalidraw`](docs/diagrams/global-architecture.excalidraw)
|
|
21
21
|
|
|
22
|
+
## Supported Harnesses
|
|
23
|
+
|
|
24
|
+
ArchGraph deploys the ARGO toolchain to all major coding-agent environments:
|
|
25
|
+
|
|
26
|
+
| Harness | MCP Server | Skills | Rules / Instructions | Agents | Wakeup Gate |
|
|
27
|
+
|--------------------|:----------:|:------:|:--------------------:|:------:|:-----------:|
|
|
28
|
+
| GitHub Copilot | ✓ | ✓ | ✓ | ✓ | — |
|
|
29
|
+
| Cursor | ✓ | ✓ | ✓ | ✓ | — |
|
|
30
|
+
| OpenCode | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
31
|
+
| DeepSeek Harness | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
32
|
+
|
|
33
|
+
A single `argo-deploy` registers the `argo` MCP server and installs all artifacts into each harness
|
|
34
|
+
automatically.
|
|
35
|
+
|
|
22
36
|
## Install
|
|
23
37
|
|
|
24
38
|
```powershell
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const crypto = require('node:crypto');
|
|
1
2
|
const fs = require('node:fs');
|
|
2
3
|
const path = require('node:path');
|
|
3
4
|
|
|
@@ -145,6 +146,23 @@ function readArchitectureDocument(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
function digestCanonicalArchitecture(architecturePath = DEFAULT_GRAPH_PATH) {
|
|
150
|
+
const absolutePath = resolveArchitecturePath(architecturePath);
|
|
151
|
+
try {
|
|
152
|
+
return crypto.createHash('sha256').update(fs.readFileSync(absolutePath)).digest('hex');
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isNeo4jGraphSyncStale(syncState, currentDigest) {
|
|
159
|
+
if (!syncState || typeof syncState !== 'object') return false;
|
|
160
|
+
if (!currentDigest || typeof currentDigest !== 'string') return false;
|
|
161
|
+
const hasDigest = typeof syncState.canonicalDigest === 'string' && syncState.canonicalDigest.length > 0;
|
|
162
|
+
if (hasDigest) return syncState.canonicalDigest !== currentDigest;
|
|
163
|
+
return typeof syncState.lastSuccessAt === 'string' && syncState.lastSuccessAt.length > 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
148
166
|
function sanitizeProps(properties) {
|
|
149
167
|
return Object.fromEntries(
|
|
150
168
|
Object.entries(properties).filter(([, value]) => value !== undefined),
|
|
@@ -226,12 +244,14 @@ function markNeo4jSyncDirty(architecturePath, error) {
|
|
|
226
244
|
|
|
227
245
|
function markNeo4jSyncClean(architecturePath, verification) {
|
|
228
246
|
const current = getNeo4jGraphSyncState(architecturePath);
|
|
247
|
+
const canonicalDigest = digestCanonicalArchitecture(architecturePath);
|
|
229
248
|
return updateNeo4jGraphSyncState(architecturePath, {
|
|
230
249
|
dirty: false,
|
|
231
250
|
lastError: undefined,
|
|
232
251
|
lastRecoveredAt: current.dirty ? new Date().toISOString() : current.lastRecoveredAt,
|
|
233
252
|
lastSuccessAt: new Date().toISOString(),
|
|
234
253
|
lastVerifiedCounts: verification ? verification.actual : current.lastVerifiedCounts,
|
|
254
|
+
canonicalDigest,
|
|
235
255
|
});
|
|
236
256
|
}
|
|
237
257
|
|
|
@@ -599,11 +619,13 @@ async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
|
599
619
|
}
|
|
600
620
|
|
|
601
621
|
const syncState = getNeo4jGraphSyncState(architecturePath);
|
|
602
|
-
|
|
622
|
+
const stale = isNeo4jGraphSyncStale(syncState, digestCanonicalArchitecture(architecturePath));
|
|
623
|
+
if (!syncState.dirty && !stale) {
|
|
603
624
|
return {
|
|
604
625
|
attempted: false,
|
|
605
626
|
eligible: true,
|
|
606
627
|
dirty: false,
|
|
628
|
+
stale: false,
|
|
607
629
|
};
|
|
608
630
|
}
|
|
609
631
|
|
|
@@ -613,6 +635,7 @@ async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
|
613
635
|
attempted: true,
|
|
614
636
|
eligible: true,
|
|
615
637
|
dirty: false,
|
|
638
|
+
stale: false,
|
|
616
639
|
status: 'passed',
|
|
617
640
|
graphKey: result.graphKey,
|
|
618
641
|
databaseProvision: result.databaseProvision,
|
|
@@ -624,6 +647,7 @@ async function recoverNeo4jSyncIfNeeded(options = {}) {
|
|
|
624
647
|
attempted: true,
|
|
625
648
|
eligible: true,
|
|
626
649
|
dirty: true,
|
|
650
|
+
stale: false,
|
|
627
651
|
status: 'failed',
|
|
628
652
|
graphKey: syncState.graphKey,
|
|
629
653
|
previousFailure: syncState.lastError,
|
|
@@ -761,11 +785,13 @@ module.exports = {
|
|
|
761
785
|
DEFAULT_GRAPH_PATH,
|
|
762
786
|
buildGraphKey,
|
|
763
787
|
createDriver,
|
|
788
|
+
digestCanonicalArchitecture,
|
|
764
789
|
ensureDatabaseExists,
|
|
765
790
|
getNeo4jGraphSyncState,
|
|
766
791
|
getNeo4jConfig,
|
|
767
792
|
getDefaultNeo4jDatabaseName,
|
|
768
793
|
isCanonicalArchitecturePath,
|
|
794
|
+
isNeo4jGraphSyncStale,
|
|
769
795
|
markNeo4jSyncDirty,
|
|
770
796
|
readArchitectureDocument,
|
|
771
797
|
readNeo4jSyncState,
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// dsh-argo-wakeup - generated by install-argo.ps1 from
|
|
2
|
+
// argo/rules/archgraph.instructions.md (single source of truth: the rule file;
|
|
3
|
+
// this file is a deployment artifact, do not edit by hand).
|
|
4
|
+
// DeepSeek Harness equivalent of the OpenCode hook argo/plugins/argo-wakeup.js:
|
|
5
|
+
// registers the unconditional wakeup gate as the first system-prompt section
|
|
6
|
+
// after the harness identity, so every session identifies its Business Actor
|
|
7
|
+
// through the argo MCP server before responding.
|
|
8
|
+
export const name = 'dsh-argo-wakeup'
|
|
9
|
+
export const inject = ['systemPrompt']
|
|
10
|
+
const WAKEUP_GATE = "STEP 0 — UNCONDITIONAL STARTUP GATE. This applies to the very first message of every session, including greetings, casual chat, and questions; it may never be skipped, deferred, or treated as optional.\r\n1. Your very FIRST tool call in the session MUST be an ARGO MCP query that lists all `Business Actor`s (getSystemArchitecture with purpose \"audit\" and subject \"Business Actor\"). Identify which `Business Actor` you are. If you are not sure, consult your human partner to confirm your role before doing anything else.\r\n2. Restore your long-term memory: the SUBVIEW hierarchy mounted under that `Business Actor` element — the Views whose `parent_element_id` points to this Actor, plus the elements, relationships, and further nested sub-views inside them (NOT the View that merely includes the Actor in its `included_elements`) — and load it into session memory.\r\n3. If the `agent` attribute of the confirmed Actor differs from your current Agent type, switch to that Agent type, or delegate to an Agent of that type per `\u003cCoperationGuideline\u003e` item 2.\r\n4. If the ARGO MCP is unavailable or errors, state that explicitly to the user before doing anything else. Only after completing this gate may you respond to the user or take any other action."
|
|
11
|
+
export function apply(ctx) {
|
|
12
|
+
ctx.effect(() => ctx.systemPrompt.section({
|
|
13
|
+
name: 'argo:wakeup',
|
|
14
|
+
order: -90,
|
|
15
|
+
text: WAKEUP_GATE,
|
|
16
|
+
}), 'argo:wakeup.section()')
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// dsh-argo-workspace - generated by install-argo.ps1 (single source of truth:
|
|
2
|
+
// the installer; this file is a deployment artifact, do not edit by hand).
|
|
3
|
+
//
|
|
4
|
+
// Direct MCP bridge to the argo server for DeepSeek Harness. Registers every
|
|
5
|
+
// argo tool as mcp__argo__* and injects the current session's workspace
|
|
6
|
+
// directory (SessionHeader.cwd) as the per-call `workspaceRoot` argument, so
|
|
7
|
+
// one dsh instance follows whichever workspace the user switched to - the
|
|
8
|
+
// model sees no extra parameters and no internal tool names. The argo server
|
|
9
|
+
// honors the injected workspaceRoot unconditionally.
|
|
10
|
+
//
|
|
11
|
+
// Zero dependencies on purpose: implements the minimal MCP stdio client
|
|
12
|
+
// (JSON-RPC 2.0, one JSON object per line) with Node built-ins only, so the
|
|
13
|
+
// plugin runs from any DSH layout without resolving @modelcontextprotocol/sdk.
|
|
14
|
+
import { spawn } from 'node:child_process'
|
|
15
|
+
import readline from 'node:readline'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
|
|
18
|
+
export const name = 'dsh-argo-workspace'
|
|
19
|
+
export const inject = ['tools']
|
|
20
|
+
|
|
21
|
+
// 包内默认:<pkg>/argo/scripts/argo-mcp-server.js —— 随包走,跨机器可移植。
|
|
22
|
+
const DEFAULT_SERVER_PATH = fileURLToPath(
|
|
23
|
+
new URL('../argo/scripts/argo-mcp-server.js', import.meta.url),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
/** Minimal MCP stdio client over the argo server process. */
|
|
27
|
+
function createArgoClient(serverPath, env, cwd) {
|
|
28
|
+
const child = spawn('node', [serverPath], {
|
|
29
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
30
|
+
env,
|
|
31
|
+
...(cwd ? { cwd } : {}),
|
|
32
|
+
})
|
|
33
|
+
const pending = new Map()
|
|
34
|
+
let nextId = 1
|
|
35
|
+
readline.createInterface({ input: child.stdout }).on('line', (line) => {
|
|
36
|
+
let message
|
|
37
|
+
try { message = JSON.parse(line) } catch { return }
|
|
38
|
+
if (message && typeof message.id === 'number' && pending.has(message.id)) {
|
|
39
|
+
const { resolve, reject } = pending.get(message.id)
|
|
40
|
+
pending.delete(message.id)
|
|
41
|
+
if (message.error) reject(new Error(JSON.stringify(message.error)))
|
|
42
|
+
else resolve(message.result)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
child.stderr.on('data', () => {}) // drain; the argo server logs to stderr
|
|
46
|
+
const request = (method, params) => new Promise((resolve, reject) => {
|
|
47
|
+
const id = nextId++
|
|
48
|
+
pending.set(id, { resolve, reject })
|
|
49
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n')
|
|
50
|
+
})
|
|
51
|
+
return {
|
|
52
|
+
request,
|
|
53
|
+
notify: (method, params) => {
|
|
54
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n')
|
|
55
|
+
},
|
|
56
|
+
close: () => child.kill(),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Connect, list tools, register them, and keep the client until disposal. */
|
|
61
|
+
export async function apply(ctx, config) {
|
|
62
|
+
const serverPath = config.serverPath
|
|
63
|
+
?? process.env.ARGO_SERVER_PATH
|
|
64
|
+
?? DEFAULT_SERVER_PATH
|
|
65
|
+
const workspaces = Array.isArray(config.workspaces) ? config.workspaces : []
|
|
66
|
+
const env = { ...process.env }
|
|
67
|
+
if (workspaces.length > 0) env.ARGO_WORKSPACE_ROOTS = workspaces.join(';')
|
|
68
|
+
const client = createArgoClient(serverPath, env, config.cwd)
|
|
69
|
+
ctx.effect(() => () => client.close(), 'dsh-argo-workspace.dispose()')
|
|
70
|
+
|
|
71
|
+
let tools = []
|
|
72
|
+
try {
|
|
73
|
+
await client.request('initialize', {
|
|
74
|
+
protocolVersion: '2024-11-05',
|
|
75
|
+
capabilities: {},
|
|
76
|
+
clientInfo: { name: 'dsh-argo-workspace', version: '0.1.0' },
|
|
77
|
+
})
|
|
78
|
+
client.notify('notifications/initialized', {})
|
|
79
|
+
const listed = await client.request('tools/list', {})
|
|
80
|
+
tools = (listed && Array.isArray(listed.tools)) ? listed.tools : []
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn(`[dsh-argo-workspace] failed to connect to the argo MCP server (${serverPath}): ${error && error.message ? error.message : error}`)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const disposers = []
|
|
87
|
+
for (const tool of tools) {
|
|
88
|
+
const publicName = 'mcp__argo__' + tool.name
|
|
89
|
+
disposers.push(ctx.tools.register({
|
|
90
|
+
name: publicName,
|
|
91
|
+
description: tool.description ?? '',
|
|
92
|
+
parameters: tool.inputSchema,
|
|
93
|
+
output: {
|
|
94
|
+
schema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
content: { type: 'array', items: {} },
|
|
98
|
+
structuredContent: {},
|
|
99
|
+
},
|
|
100
|
+
required: ['content'],
|
|
101
|
+
additionalProperties: false,
|
|
102
|
+
},
|
|
103
|
+
render: (_args, value) => value.content,
|
|
104
|
+
},
|
|
105
|
+
execute: async (args, exec) => {
|
|
106
|
+
// SessionHeader.cwd (the durable session workspace), NOT
|
|
107
|
+
// requestHeader() — that returns the request EpochHeader
|
|
108
|
+
// (config/system/tools) which has no cwd field.
|
|
109
|
+
const sessionCwd = exec && exec.agent && exec.agent.session
|
|
110
|
+
? exec.agent.session.header?.cwd
|
|
111
|
+
: undefined
|
|
112
|
+
const injected = { ...(args && typeof args === 'object' ? args : {}) }
|
|
113
|
+
if (typeof sessionCwd === 'string' && sessionCwd !== '') {
|
|
114
|
+
injected.workspaceRoot = sessionCwd
|
|
115
|
+
}
|
|
116
|
+
if (exec && exec.signal && exec.signal.aborted) {
|
|
117
|
+
throw new Error('aborted')
|
|
118
|
+
}
|
|
119
|
+
const result = await client.request('tools/call', {
|
|
120
|
+
name: tool.name,
|
|
121
|
+
arguments: injected,
|
|
122
|
+
})
|
|
123
|
+
const text = Array.isArray(result.content)
|
|
124
|
+
? result.content
|
|
125
|
+
.map((block) => block && block.type === 'text' && typeof block.text === 'string'
|
|
126
|
+
? block.text
|
|
127
|
+
: JSON.stringify(block))
|
|
128
|
+
.join('\n')
|
|
129
|
+
: (result.toolResult !== undefined ? JSON.stringify(result.toolResult) : '(no output)')
|
|
130
|
+
if (result.isError === true) throw new Error(text)
|
|
131
|
+
return {
|
|
132
|
+
content: [{ type: 'text', text }],
|
|
133
|
+
...(result.structuredContent !== undefined ? { structuredContent: result.structuredContent } : {}),
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
}))
|
|
137
|
+
}
|
|
138
|
+
ctx.effect(() => () => {
|
|
139
|
+
for (const dispose of disposers) dispose()
|
|
140
|
+
}, 'dsh-argo-workspace.tools')
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
package/install-argo.ps1
CHANGED
|
@@ -726,7 +726,7 @@ if ($SkipDsh) {
|
|
|
726
726
|
Write-DshManagedBlock -Path $patchPath -Block $block -MarkerStart '# BEGIN ArchGraph ARGO deployment' -MarkerEnd '# END ArchGraph ARGO deployment'
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
-
Write-Host "[19/19]
|
|
729
|
+
Write-Host "[19/19] argo\agents -> $DshHome\.agent-presets\<id> (DeepSeek Harness agent presets)"
|
|
730
730
|
New-DshAgentPresets -DshHome $DshHome -AgentsSrc (Join-Path $argoDir 'agents')
|
|
731
731
|
|
|
732
732
|
Write-Host ' Restart `dsh web` to activate the MCP bridge and the wakeup plugin;'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archgraph-argo",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.10",
|
|
4
4
|
"description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -17,11 +17,28 @@
|
|
|
17
17
|
"argo/package.json",
|
|
18
18
|
"vendor",
|
|
19
19
|
"install-argo.ps1",
|
|
20
|
-
"bin"
|
|
20
|
+
"bin",
|
|
21
|
+
"cordis.patch.yml",
|
|
22
|
+
"dsh-argo-workspace",
|
|
23
|
+
"dsh-argo-wakeup"
|
|
21
24
|
],
|
|
22
|
-
"
|
|
25
|
+
"exports": {
|
|
26
|
+
"./dsh-argo-workspace": "./dsh-argo-workspace/index.js",
|
|
27
|
+
"./dsh-argo-wakeup": "./dsh-argo-wakeup/index.js",
|
|
28
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"dsh": {
|
|
32
|
+
"bundle": {
|
|
33
|
+
"patch": "./cordis.patch.yml"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
23
37
|
"neo4j-driver": "^6.2.0"
|
|
24
38
|
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@resvg/resvg-js": "^2.6.2"
|
|
41
|
+
},
|
|
25
42
|
"scripts": {
|
|
26
43
|
"test": "node --test \"tests/*.test.js\""
|
|
27
44
|
},
|