@gwdn/nexus-mcp 0.8.6 → 0.8.8
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 +3 -4
- package/dist/nexus-api.d.ts.map +1 -1
- package/dist/nexus-api.js +10 -1
- package/dist/nexus-api.js.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +9 -2
- package/.github/workflows/ci.yml +0 -38
- package/.github/workflows/release.yml +0 -57
- package/src/__tests__/auth.test.ts +0 -162
- package/src/__tests__/decision-comments.test.ts +0 -173
- package/src/__tests__/e2e-integration.test.ts +0 -443
- package/src/__tests__/helpers.ts +0 -58
- package/src/__tests__/layer1-knowledge.test.ts +0 -302
- package/src/__tests__/layer2-coordination.test.ts +0 -918
- package/src/__tests__/layer3-governance.test.ts +0 -205
- package/src/__tests__/machine-id.test.ts +0 -136
- package/src/__tests__/nexus-api.test.ts +0 -127
- package/src/__tests__/project-list-and-skill-assign.test.ts +0 -282
- package/src/__tests__/reviews.test.ts +0 -420
- package/src/__tests__/server.test.ts +0 -251
- package/src/__tests__/setup.ts +0 -15
- package/src/__tests__/task-list-and-doc-list.test.ts +0 -226
- package/src/auth.ts +0 -81
- package/src/machine-id.ts +0 -60
- package/src/nexus-api.ts +0 -141
- package/src/server.ts +0 -629
- package/src/tools/add-task-note.ts +0 -50
- package/src/tools/append-session-entry.ts +0 -113
- package/src/tools/classify-document.ts +0 -55
- package/src/tools/create-task.ts +0 -66
- package/src/tools/decision-comments.ts +0 -103
- package/src/tools/delete-document.ts +0 -43
- package/src/tools/directives.ts +0 -303
- package/src/tools/get-document.ts +0 -80
- package/src/tools/get-project-memory.ts +0 -65
- package/src/tools/get-related-entities.ts +0 -73
- package/src/tools/governance.ts +0 -165
- package/src/tools/ingest-document.ts +0 -65
- package/src/tools/letter-inbox.ts +0 -157
- package/src/tools/letters.ts +0 -144
- package/src/tools/list-documents.ts +0 -58
- package/src/tools/list-tasks.ts +0 -57
- package/src/tools/project-list.ts +0 -52
- package/src/tools/reviews.ts +0 -281
- package/src/tools/search-knowledge.ts +0 -68
- package/src/tools/sessions.ts +0 -157
- package/src/tools/skill-assign.ts +0 -142
- package/src/tools/skills.ts +0 -255
- package/src/tools/update-document.ts +0 -66
- package/src/tools/update-task-status.ts +0 -64
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -8
package/README.md
CHANGED
|
@@ -89,12 +89,11 @@ terminals that were started before the update.
|
|
|
89
89
|
|
|
90
90
|
**Symptoms:** MCP tools report an older version, or new tools/fixes are missing.
|
|
91
91
|
|
|
92
|
-
**Fix:** Before starting OpenCode, clear
|
|
92
|
+
**Fix:** Before starting OpenCode, clear all npx caches for this package:
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
|
-
find ~/.npm/_npx -path "*/node_modules/@gwdn/nexus-mcp
|
|
96
|
-
-exec
|
|
97
|
-
| xargs rm -rf
|
|
95
|
+
find ~/.npm/_npx -path "*/node_modules/@gwdn/nexus-mcp" -type d \
|
|
96
|
+
-exec rm -rf {} + 2>/dev/null
|
|
98
97
|
```
|
|
99
98
|
|
|
100
99
|
Then start OpenCode normally. npx will fetch the latest version into a fresh
|
package/dist/nexus-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nexus-api.d.ts","sourceRoot":"","sources":["../src/nexus-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"nexus-api.d.ts","sourceRoot":"","sources":["../src/nexus-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsBH;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAIrC;AA0BD,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,OAAO;IAC3C,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,CAAC,GAAG,IAAI,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAKD;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EACxC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;CACd,GACL,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CA8C9B;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAE9B;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAE9B"}
|
package/dist/nexus-api.js
CHANGED
|
@@ -5,17 +5,25 @@
|
|
|
5
5
|
* All operations go through /api/mcp/* endpoints, authenticated
|
|
6
6
|
* via the NEXUS_PRIVATE_TOKEN (nxs_pat_*) Bearer token.
|
|
7
7
|
*/
|
|
8
|
+
import { hostname } from 'node:os';
|
|
8
9
|
import { getMachineId } from './machine-id.js';
|
|
9
10
|
let _baseUrl = null;
|
|
10
11
|
let _token = null;
|
|
11
12
|
let _machineId = null;
|
|
12
|
-
|
|
13
|
+
let _machineName = null;
|
|
14
|
+
// Eagerly resolve machine ID and hostname (sync, cached after first call)
|
|
13
15
|
try {
|
|
14
16
|
_machineId = getMachineId();
|
|
15
17
|
}
|
|
16
18
|
catch {
|
|
17
19
|
// machine_id is optional enrichment — never block startup
|
|
18
20
|
}
|
|
21
|
+
try {
|
|
22
|
+
_machineName = hostname();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// hostname is optional enrichment
|
|
26
|
+
}
|
|
19
27
|
/**
|
|
20
28
|
* Reset the cached API configuration. Used by E2E tests to switch
|
|
21
29
|
* from mocked env vars to real credentials.
|
|
@@ -54,6 +62,7 @@ export async function nexusApi(path, options = {}) {
|
|
|
54
62
|
Authorization: `Bearer ${token}`,
|
|
55
63
|
'Content-Type': 'application/json',
|
|
56
64
|
...(_machineId ? { 'X-Nexus-Machine-Id': _machineId } : {}),
|
|
65
|
+
...(_machineName ? { 'X-Nexus-Machine-Name': _machineName } : {}),
|
|
57
66
|
...(options.headers ?? {}),
|
|
58
67
|
};
|
|
59
68
|
try {
|
package/dist/nexus-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nexus-api.js","sourceRoot":"","sources":["../src/nexus-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,IAAI,QAAQ,GAAkB,IAAI,CAAA;AAClC,IAAI,MAAM,GAAkB,IAAI,CAAA;AAChC,IAAI,UAAU,GAAkB,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"nexus-api.js","sourceRoot":"","sources":["../src/nexus-api.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -121,7 +121,7 @@ function withIdentity(handler) {
|
|
|
121
121
|
// ---------------------------------------------------------------------------
|
|
122
122
|
const server = new McpServer({
|
|
123
123
|
name: 'nexus-mcp',
|
|
124
|
-
version: '0.8.
|
|
124
|
+
version: '0.8.7',
|
|
125
125
|
}, {
|
|
126
126
|
capabilities: {
|
|
127
127
|
tools: {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gwdn/nexus-mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "MCP server for the Nexus platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"nexus-mcp": "dist/server.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
11
|
+
"build": "tsc && chmod +x dist/server.js",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
12
13
|
"dev": "tsx src/server.ts",
|
|
13
14
|
"start": "node dist/server.js",
|
|
14
15
|
"test": "vitest run",
|
|
@@ -20,10 +21,16 @@
|
|
|
20
21
|
"zod": "^4.3.6"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
24
|
+
"@types/node": "^25.9.1",
|
|
23
25
|
"tsx": "^4.21.0",
|
|
24
26
|
"typescript": "^5.8.3",
|
|
25
27
|
"vitest": "^4.1.4"
|
|
26
28
|
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
27
34
|
"engines": {
|
|
28
35
|
"node": ">=20"
|
|
29
36
|
}
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
name: Test (Node ${{ matrix.node-version }})
|
|
12
|
-
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
13
|
-
strategy:
|
|
14
|
-
fail-fast: false
|
|
15
|
-
matrix:
|
|
16
|
-
node-version: [20, 22]
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- name: Checkout
|
|
20
|
-
uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
- name: Setup Node.js ${{ matrix.node-version }}
|
|
23
|
-
uses: actions/setup-node@v4
|
|
24
|
-
with:
|
|
25
|
-
node-version: ${{ matrix.node-version }}
|
|
26
|
-
cache: npm
|
|
27
|
-
|
|
28
|
-
- name: Install dependencies
|
|
29
|
-
run: npm ci
|
|
30
|
-
|
|
31
|
-
- name: Typecheck
|
|
32
|
-
run: npm run typecheck
|
|
33
|
-
|
|
34
|
-
- name: Test
|
|
35
|
-
run: npm test
|
|
36
|
-
|
|
37
|
-
- name: Build
|
|
38
|
-
run: npm run build
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
release:
|
|
13
|
-
name: Publish to npm
|
|
14
|
-
runs-on: blacksmith-2vcpu-ubuntu-2204
|
|
15
|
-
|
|
16
|
-
steps:
|
|
17
|
-
- name: Checkout
|
|
18
|
-
uses: actions/checkout@v4
|
|
19
|
-
|
|
20
|
-
- name: Setup Node.js
|
|
21
|
-
uses: actions/setup-node@v4
|
|
22
|
-
with:
|
|
23
|
-
node-version: 22
|
|
24
|
-
cache: npm
|
|
25
|
-
registry-url: https://registry.npmjs.org
|
|
26
|
-
|
|
27
|
-
- name: Install dependencies
|
|
28
|
-
run: npm ci
|
|
29
|
-
|
|
30
|
-
- name: Typecheck
|
|
31
|
-
run: npm run typecheck
|
|
32
|
-
|
|
33
|
-
- name: Test
|
|
34
|
-
run: npm test
|
|
35
|
-
|
|
36
|
-
- name: Build
|
|
37
|
-
run: npm run build
|
|
38
|
-
|
|
39
|
-
- name: Verify version matches tag
|
|
40
|
-
run: |
|
|
41
|
-
PKG_VERSION="v$(node -p "require('./package.json').version")"
|
|
42
|
-
TAG_VERSION="${GITHUB_REF#refs/tags/}"
|
|
43
|
-
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
|
|
44
|
-
echo "ERROR: package.json version ($PKG_VERSION) does not match tag ($TAG_VERSION)"
|
|
45
|
-
exit 1
|
|
46
|
-
fi
|
|
47
|
-
echo "Version verified: $TAG_VERSION"
|
|
48
|
-
|
|
49
|
-
- name: Publish to npm
|
|
50
|
-
run: npm publish --access public
|
|
51
|
-
env:
|
|
52
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
53
|
-
|
|
54
|
-
- name: Create GitHub Release
|
|
55
|
-
uses: softprops/action-gh-release@v2
|
|
56
|
-
with:
|
|
57
|
-
generate_release_notes: true
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for src/auth.ts
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - initIdentity() with valid/invalid tokens
|
|
6
|
-
* - getIdentity() before/after init
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
10
|
-
|
|
11
|
-
// Mock the nexus-api module before importing auth
|
|
12
|
-
vi.mock('../nexus-api.js', () => ({
|
|
13
|
-
nexusGet: vi.fn(),
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
import { nexusGet } from '../nexus-api.js'
|
|
17
|
-
import { TEST_IDS } from './helpers'
|
|
18
|
-
|
|
19
|
-
describe('MCP Auth', () => {
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
vi.resetModules()
|
|
22
|
-
vi.restoreAllMocks()
|
|
23
|
-
// Suppress console.error
|
|
24
|
-
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe('initIdentity', () => {
|
|
28
|
-
it('should resolve identity from a valid token', async () => {
|
|
29
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
30
|
-
ok: true,
|
|
31
|
-
status: 200,
|
|
32
|
-
data: {
|
|
33
|
-
userId: TEST_IDS.userId,
|
|
34
|
-
email: 'test@example.com',
|
|
35
|
-
displayName: 'Test User',
|
|
36
|
-
isPlatformAdmin: true,
|
|
37
|
-
isPlatformOwner: false,
|
|
38
|
-
tenantId: TEST_IDS.tenantId,
|
|
39
|
-
memberships: [],
|
|
40
|
-
agentAssignments: [],
|
|
41
|
-
},
|
|
42
|
-
error: null,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
process.env.NEXUS_PRIVATE_TOKEN =
|
|
46
|
-
'nxs_pat_valid-test-token-1234567890abcde'
|
|
47
|
-
|
|
48
|
-
const { initIdentity } = await import('../auth.js')
|
|
49
|
-
const identity = await initIdentity()
|
|
50
|
-
|
|
51
|
-
expect(identity).toBeDefined()
|
|
52
|
-
expect(identity.userId).toBe(TEST_IDS.userId)
|
|
53
|
-
expect(identity.displayName).toBe('Test User')
|
|
54
|
-
expect(identity.email).toBe('test@example.com')
|
|
55
|
-
expect(identity.isPlatformAdmin).toBe(true)
|
|
56
|
-
expect(identity.isPlatformOwner).toBe(false)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should throw if NEXUS_PRIVATE_TOKEN is not set', async () => {
|
|
60
|
-
delete process.env.NEXUS_PRIVATE_TOKEN
|
|
61
|
-
|
|
62
|
-
const { initIdentity } = await import('../auth.js')
|
|
63
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
64
|
-
'NEXUS_PRIVATE_TOKEN is not set',
|
|
65
|
-
)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should throw if API returns an error', async () => {
|
|
69
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
70
|
-
ok: false,
|
|
71
|
-
status: 401,
|
|
72
|
-
data: null,
|
|
73
|
-
error: 'Invalid token',
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_invalid-token-123456789012345'
|
|
77
|
-
|
|
78
|
-
const { initIdentity } = await import('../auth.js')
|
|
79
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
80
|
-
'Identity resolution failed',
|
|
81
|
-
)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('should throw if API returns null data', async () => {
|
|
85
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
86
|
-
ok: true,
|
|
87
|
-
status: 200,
|
|
88
|
-
data: null,
|
|
89
|
-
error: null,
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_null-data-token-12345678901234'
|
|
93
|
-
|
|
94
|
-
const { initIdentity } = await import('../auth.js')
|
|
95
|
-
await expect(initIdentity()).rejects.toThrow(
|
|
96
|
-
'Identity resolution failed',
|
|
97
|
-
)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should resolve platform_owner identity with both admin and owner flags', async () => {
|
|
101
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
102
|
-
ok: true,
|
|
103
|
-
status: 200,
|
|
104
|
-
data: {
|
|
105
|
-
userId: TEST_IDS.userId,
|
|
106
|
-
email: 'owner@example.com',
|
|
107
|
-
displayName: 'Platform Owner',
|
|
108
|
-
isPlatformAdmin: true,
|
|
109
|
-
isPlatformOwner: true,
|
|
110
|
-
tenantId: TEST_IDS.tenantId,
|
|
111
|
-
memberships: [],
|
|
112
|
-
agentAssignments: [],
|
|
113
|
-
},
|
|
114
|
-
error: null,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_owner-test-token-12345678901'
|
|
118
|
-
|
|
119
|
-
const { initIdentity } = await import('../auth.js')
|
|
120
|
-
const identity = await initIdentity()
|
|
121
|
-
|
|
122
|
-
expect(identity.isPlatformAdmin).toBe(true)
|
|
123
|
-
expect(identity.isPlatformOwner).toBe(true)
|
|
124
|
-
expect(identity.displayName).toBe('Platform Owner')
|
|
125
|
-
expect(identity.email).toBe('owner@example.com')
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
describe('getIdentity', () => {
|
|
130
|
-
it('should throw if called before initIdentity', async () => {
|
|
131
|
-
const { getIdentity } = await import('../auth.js')
|
|
132
|
-
expect(() => getIdentity()).toThrow('Identity not initialized')
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('should return cached identity after init', async () => {
|
|
136
|
-
vi.mocked(nexusGet).mockResolvedValue({
|
|
137
|
-
ok: true,
|
|
138
|
-
status: 200,
|
|
139
|
-
data: {
|
|
140
|
-
userId: TEST_IDS.userId,
|
|
141
|
-
email: 'cached@example.com',
|
|
142
|
-
displayName: 'Cached User',
|
|
143
|
-
isPlatformAdmin: false,
|
|
144
|
-
isPlatformOwner: false,
|
|
145
|
-
tenantId: TEST_IDS.tenantId,
|
|
146
|
-
memberships: [],
|
|
147
|
-
agentAssignments: [],
|
|
148
|
-
},
|
|
149
|
-
error: null,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
process.env.NEXUS_PRIVATE_TOKEN = 'nxs_pat_cached-test-token-1234567890'
|
|
153
|
-
|
|
154
|
-
const { initIdentity, getIdentity } = await import('../auth.js')
|
|
155
|
-
await initIdentity()
|
|
156
|
-
|
|
157
|
-
const identity = getIdentity()
|
|
158
|
-
expect(identity.userId).toBe(TEST_IDS.userId)
|
|
159
|
-
expect(identity.displayName).toBe('Cached User')
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
})
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for decision comment tools.
|
|
3
|
-
* All tools delegate to the Nexus API via nexusPost().
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
-
import { mockApiError, mockApiSuccess, parseToolResponse, TEST_IDS } from './helpers'
|
|
8
|
-
|
|
9
|
-
vi.mock('../nexus-api.js', () => ({
|
|
10
|
-
nexusPost: vi.fn(),
|
|
11
|
-
}))
|
|
12
|
-
|
|
13
|
-
import { nexusPost } from '../nexus-api.js'
|
|
14
|
-
|
|
15
|
-
describe('Decision Comments tools', () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
vi.restoreAllMocks()
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
describe('addDecisionComment', () => {
|
|
21
|
-
it('should add a comment to an existing decision', async () => {
|
|
22
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
23
|
-
mockApiSuccess({
|
|
24
|
-
action: 'dc_add',
|
|
25
|
-
comment_id: 'comment-id-1',
|
|
26
|
-
decision_id: TEST_IDS.adrId,
|
|
27
|
-
decision_title: 'Test ADR',
|
|
28
|
-
project_id: TEST_IDS.projectId,
|
|
29
|
-
}),
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
33
|
-
const result = await addDecisionComment({
|
|
34
|
-
decision_id: TEST_IDS.adrId,
|
|
35
|
-
body: 'This is a comment',
|
|
36
|
-
user_id: TEST_IDS.userId,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
expect(result.isError).toBeUndefined()
|
|
40
|
-
const parsed = parseToolResponse(result)
|
|
41
|
-
expect(parsed.action).toBe('dc_add')
|
|
42
|
-
expect(parsed.comment_id).toBe('comment-id-1')
|
|
43
|
-
expect(parsed.decision_id).toBe(TEST_IDS.adrId)
|
|
44
|
-
expect(parsed.decision_title).toBe('Test ADR')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should support agent_id in comment', async () => {
|
|
48
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
49
|
-
mockApiSuccess({
|
|
50
|
-
action: 'dc_add',
|
|
51
|
-
comment_id: 'comment-id-2',
|
|
52
|
-
decision_id: TEST_IDS.adrId,
|
|
53
|
-
decision_title: 'ADR',
|
|
54
|
-
project_id: TEST_IDS.projectId,
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
59
|
-
const result = await addDecisionComment({
|
|
60
|
-
decision_id: TEST_IDS.adrId,
|
|
61
|
-
body: 'Agent comment',
|
|
62
|
-
agent_id: TEST_IDS.agentId,
|
|
63
|
-
user_id: TEST_IDS.userId,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
expect(result.isError).toBeUndefined()
|
|
67
|
-
// Verify agent_id was passed to the API
|
|
68
|
-
expect(nexusPost).toHaveBeenCalledWith('/api/mcp/governance', {
|
|
69
|
-
action: 'dc_add',
|
|
70
|
-
decision_id: TEST_IDS.adrId,
|
|
71
|
-
body: 'Agent comment',
|
|
72
|
-
agent_id: TEST_IDS.agentId,
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('should return error if decision not found', async () => {
|
|
77
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Decision not found', 404))
|
|
78
|
-
|
|
79
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
80
|
-
const result = await addDecisionComment({
|
|
81
|
-
decision_id: TEST_IDS.adrId,
|
|
82
|
-
body: 'Comment',
|
|
83
|
-
user_id: TEST_IDS.userId,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
expect(result.isError).toBe(true)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should return error on API failure', async () => {
|
|
90
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Failed to add comment'))
|
|
91
|
-
|
|
92
|
-
const { addDecisionComment } = await import('../tools/decision-comments.js')
|
|
93
|
-
const result = await addDecisionComment({
|
|
94
|
-
decision_id: TEST_IDS.adrId,
|
|
95
|
-
body: 'Comment',
|
|
96
|
-
user_id: TEST_IDS.userId,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
expect(result.isError).toBe(true)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
describe('listDecisionComments', () => {
|
|
104
|
-
it('should list comments for a decision', async () => {
|
|
105
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
106
|
-
mockApiSuccess({
|
|
107
|
-
decision_id: TEST_IDS.adrId,
|
|
108
|
-
decision_title: 'Test ADR',
|
|
109
|
-
total: 2,
|
|
110
|
-
comments: [
|
|
111
|
-
{
|
|
112
|
-
id: 'c1',
|
|
113
|
-
author_id: TEST_IDS.userId,
|
|
114
|
-
agent_id: null,
|
|
115
|
-
body: 'First comment',
|
|
116
|
-
created_at: '2026-01-01T00:00:00Z',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
id: 'c2',
|
|
120
|
-
author_id: TEST_IDS.userId,
|
|
121
|
-
agent_id: TEST_IDS.agentId,
|
|
122
|
-
body: 'Second comment',
|
|
123
|
-
created_at: '2026-01-01T01:00:00Z',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
}),
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
130
|
-
const result = await listDecisionComments({
|
|
131
|
-
decision_id: TEST_IDS.adrId,
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
expect(result.isError).toBeUndefined()
|
|
135
|
-
const parsed = parseToolResponse(result)
|
|
136
|
-
expect(parsed.decision_title).toBe('Test ADR')
|
|
137
|
-
expect(parsed.total).toBe(2)
|
|
138
|
-
expect(parsed.comments).toHaveLength(2)
|
|
139
|
-
expect(parsed.comments[0].body).toBe('First comment')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('should return empty list for decision with no comments', async () => {
|
|
143
|
-
vi.mocked(nexusPost).mockResolvedValue(
|
|
144
|
-
mockApiSuccess({
|
|
145
|
-
decision_id: TEST_IDS.adrId,
|
|
146
|
-
decision_title: 'ADR',
|
|
147
|
-
total: 0,
|
|
148
|
-
comments: [],
|
|
149
|
-
}),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
153
|
-
const result = await listDecisionComments({
|
|
154
|
-
decision_id: TEST_IDS.adrId,
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
const parsed = parseToolResponse(result)
|
|
158
|
-
expect(parsed.total).toBe(0)
|
|
159
|
-
expect(parsed.comments).toHaveLength(0)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('should return error if decision not found', async () => {
|
|
163
|
-
vi.mocked(nexusPost).mockResolvedValue(mockApiError('Decision not found', 404))
|
|
164
|
-
|
|
165
|
-
const { listDecisionComments } = await import('../tools/decision-comments.js')
|
|
166
|
-
const result = await listDecisionComments({
|
|
167
|
-
decision_id: TEST_IDS.adrId,
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
expect(result.isError).toBe(true)
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
})
|