@agentmap/opencode 0.4.0 → 0.6.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/CHANGELOG.md +35 -0
- package/README.md +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -111
- package/dist/index.js.map +1 -1
- package/package.json +15 -3
- package/src/index.ts +78 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
1. **Error toast notifications** — plugin errors now surface as OpenCode TUI toast notifications instead of being silently swallowed or polluting the terminal UI.
|
|
6
|
+
|
|
7
|
+
2. **Updated to use agentmap 0.9.0** — benefits from recursive submodule trees, symlink filtering, duplicate file detection, and compressed output.
|
|
8
|
+
|
|
9
|
+
## 0.5.0
|
|
10
|
+
|
|
11
|
+
- Update to use agentmap 0.8.0 with submodule support
|
|
12
|
+
- Submodules now appear in the generated codebase map with branch and commit info
|
|
13
|
+
|
|
14
|
+
## 0.4.0
|
|
15
|
+
|
|
16
|
+
- Include git diff info in generated map by default
|
|
17
|
+
- Update to use agentmap 0.7.0
|
|
18
|
+
|
|
19
|
+
## 0.3.0
|
|
20
|
+
|
|
21
|
+
- Show only exported symbols when truncating files with many definitions
|
|
22
|
+
- Add `<agentmap-instructions>` section with guidance for maintaining file descriptions
|
|
23
|
+
- Safety checks now handled by agentmap library (non-git repos, home directory)
|
|
24
|
+
- Update to use agentmap 0.6.0
|
|
25
|
+
|
|
26
|
+
## 0.2.0
|
|
27
|
+
|
|
28
|
+
- Update to use agentmap 0.3.0 with new diff features
|
|
29
|
+
- Improved system prompt description
|
|
30
|
+
|
|
31
|
+
## 0.1.0
|
|
32
|
+
|
|
33
|
+
- Initial release
|
|
34
|
+
- OpenCode plugin that injects codebase map into system prompt
|
|
35
|
+
- Uses `experimental.chat.system.transform` hook to inject map wrapped in `<agentmap>` tags
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @agentmap/opencode
|
|
2
|
+
|
|
3
|
+
OpenCode plugin that injects an `agentmap` codebase map into the system prompt at session time.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @agentmap/opencode
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## OpenCode config
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"plugin": ["@agentmap/opencode"]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Restart OpenCode after updating the plugin list.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAOjD,eAAO,MAAM,cAAc,EAAE,MAmE5B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,104 +1,25 @@
|
|
|
1
|
-
// @agentmap
|
|
2
1
|
// OpenCode plugin that injects codebase map into system prompt.
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { formatLogMessage } from 'agentmap/src/logger';
|
|
3
|
+
import { generateMapYaml } from 'agentmap/src/index';
|
|
5
4
|
const MAX_LINES = 1000;
|
|
6
|
-
|
|
7
|
-
* Check if a def value indicates exported or extern
|
|
8
|
-
*/
|
|
9
|
-
function isExportedDef(value) {
|
|
10
|
-
return value.includes('exported') || value.includes('extern');
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Truncate definitions in a file entry to MAX_DEFS_PER_FILE
|
|
14
|
-
* If file has exported symbols, shows only exports field instead
|
|
15
|
-
* Otherwise uses current truncation behavior
|
|
16
|
-
*/
|
|
17
|
-
function truncateDefs(entry) {
|
|
18
|
-
if (!entry.defs)
|
|
19
|
-
return entry;
|
|
20
|
-
const defNames = Object.keys(entry.defs);
|
|
21
|
-
if (defNames.length <= MAX_DEFS_PER_FILE)
|
|
22
|
-
return entry;
|
|
23
|
-
// Filter to only exported/extern definitions
|
|
24
|
-
const exportedNames = defNames.filter(name => isExportedDef(entry.defs[name]));
|
|
25
|
-
// If we have exports, use exports field instead of defs
|
|
26
|
-
if (exportedNames.length > 0) {
|
|
27
|
-
const exports = {};
|
|
28
|
-
const maxExports = Math.min(exportedNames.length, MAX_DEFS_PER_FILE);
|
|
29
|
-
for (let i = 0; i < maxExports; i++) {
|
|
30
|
-
const name = exportedNames[i];
|
|
31
|
-
exports[name] = entry.defs[name];
|
|
32
|
-
}
|
|
33
|
-
// Add marker if exports were also truncated
|
|
34
|
-
if (exportedNames.length > MAX_DEFS_PER_FILE) {
|
|
35
|
-
const remaining = exportedNames.length - MAX_DEFS_PER_FILE;
|
|
36
|
-
exports[`__more_${remaining}__`] = `${remaining} more exports`;
|
|
37
|
-
}
|
|
38
|
-
// Return with exports instead of defs
|
|
39
|
-
const { defs, ...rest } = entry;
|
|
40
|
-
return { ...rest, exports };
|
|
41
|
-
}
|
|
42
|
-
// No exports found - use current truncation behavior
|
|
43
|
-
const truncated = {};
|
|
44
|
-
for (let i = 0; i < MAX_DEFS_PER_FILE; i++) {
|
|
45
|
-
const name = defNames[i];
|
|
46
|
-
truncated[name] = entry.defs[name];
|
|
47
|
-
}
|
|
48
|
-
const remaining = defNames.length - MAX_DEFS_PER_FILE;
|
|
49
|
-
// Add marker that will be converted to comment
|
|
50
|
-
truncated[`__more_${remaining}__`] = `${remaining} more definitions`;
|
|
51
|
-
return { ...entry, defs: truncated };
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Check if a value is a FileEntry (has description or defs)
|
|
55
|
-
*/
|
|
56
|
-
function isFileEntry(value) {
|
|
57
|
-
if (!value || typeof value !== 'object')
|
|
58
|
-
return false;
|
|
59
|
-
const obj = value;
|
|
60
|
-
return 'description' in obj || 'defs' in obj;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Recursively truncate defs in all files in the map
|
|
64
|
-
*/
|
|
65
|
-
function truncateMap(node) {
|
|
66
|
-
const result = {};
|
|
67
|
-
for (const [key, value] of Object.entries(node)) {
|
|
68
|
-
if (isFileEntry(value)) {
|
|
69
|
-
result[key] = truncateDefs(value);
|
|
70
|
-
}
|
|
71
|
-
else if (value && typeof value === 'object') {
|
|
72
|
-
result[key] = truncateMap(value);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
result[key] = value;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Convert __more_N__ markers to YAML comments
|
|
82
|
-
*/
|
|
83
|
-
function markersToComments(yaml) {
|
|
84
|
-
// Match lines like: __more_25__: 25 more definitions/exports
|
|
85
|
-
// Replace with: # ... 25 more definitions/exports
|
|
86
|
-
return yaml.replace(/^(\s*)__more_(\d+)__: (\d+ more (?:definitions|exports))$/gm, '$1# ... $3');
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Truncate YAML to max lines, adding a comment if truncated
|
|
90
|
-
*/
|
|
91
|
-
function truncateLines(yaml) {
|
|
92
|
-
const lines = yaml.split('\n');
|
|
93
|
-
if (lines.length <= MAX_LINES)
|
|
94
|
-
return yaml;
|
|
95
|
-
const truncated = lines.slice(0, MAX_LINES);
|
|
96
|
-
truncated.push('# ... truncated');
|
|
97
|
-
return truncated.join('\n');
|
|
98
|
-
}
|
|
99
|
-
export const AgentMapPlugin = async ({ directory }) => {
|
|
5
|
+
export const AgentMapPlugin = async ({ directory, client }) => {
|
|
100
6
|
let cachedYaml;
|
|
101
7
|
let lastSessionID;
|
|
8
|
+
const logger = {
|
|
9
|
+
debug: () => { },
|
|
10
|
+
info: () => { },
|
|
11
|
+
warn: () => { },
|
|
12
|
+
error: (...args) => {
|
|
13
|
+
const message = formatLogMessage(args);
|
|
14
|
+
void client.tui.showToast({
|
|
15
|
+
body: {
|
|
16
|
+
title: 'agentmap',
|
|
17
|
+
message,
|
|
18
|
+
variant: 'error',
|
|
19
|
+
},
|
|
20
|
+
}).catch(() => { });
|
|
21
|
+
},
|
|
22
|
+
};
|
|
102
23
|
return {
|
|
103
24
|
'chat.message': async ({ sessionID }) => {
|
|
104
25
|
if (sessionID !== lastSessionID) {
|
|
@@ -112,20 +33,13 @@ export const AgentMapPlugin = async ({ directory }) => {
|
|
|
112
33
|
if (output.system.some((s) => s.includes('<agentmap>')))
|
|
113
34
|
return;
|
|
114
35
|
if (!cachedYaml) {
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
cachedYaml = '';
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
const truncatedMap = truncateMap(map);
|
|
124
|
-
let yaml = toYaml(truncatedMap);
|
|
125
|
-
yaml = markersToComments(yaml);
|
|
126
|
-
yaml = truncateLines(yaml);
|
|
127
|
-
cachedYaml = yaml;
|
|
36
|
+
let yaml = await generateMapYaml({ dir: directory, diff: true, logger });
|
|
37
|
+
// Truncate to max lines
|
|
38
|
+
const lines = yaml.split('\n');
|
|
39
|
+
if (lines.length > MAX_LINES) {
|
|
40
|
+
yaml = lines.slice(0, MAX_LINES).join('\n') + '\n# ... truncated';
|
|
128
41
|
}
|
|
42
|
+
cachedYaml = yaml;
|
|
129
43
|
}
|
|
130
44
|
if (!cachedYaml.trim())
|
|
131
45
|
return;
|
|
@@ -146,7 +60,7 @@ These descriptions appear in the agentmap XML at the start of every agent sessio
|
|
|
146
60
|
</agentmap-instructions>`);
|
|
147
61
|
}
|
|
148
62
|
catch (err) {
|
|
149
|
-
|
|
63
|
+
logger.error('[agentmap] Failed to generate map:', err);
|
|
150
64
|
}
|
|
151
65
|
},
|
|
152
66
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,gEAAgE;AAGhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,MAAM,SAAS,GAAG,IAAI,CAAA;AAEtB,MAAM,CAAC,MAAM,cAAc,GAAW,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE;IACpE,IAAI,UAA8B,CAAA;IAClC,IAAI,aAAiC,CAAA;IAErC,MAAM,MAAM,GAAW;QACrB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;QACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;QACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;QACd,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;YACjB,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;YACtC,KAAK,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;gBACxB,IAAI,EAAE;oBACJ,KAAK,EAAE,UAAU;oBACjB,OAAO;oBACP,OAAO,EAAE,OAAO;iBACjB;aACF,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACpB,CAAC;KACF,CAAA;IAED,OAAO;QACL,cAAc,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YACtC,IAAI,SAAS,KAAK,aAAa,EAAE,CAAC;gBAChC,aAAa,GAAG,SAAS,CAAA;gBACzB,UAAU,GAAG,SAAS,CAAA;YACxB,CAAC;QACH,CAAC;QAED,oCAAoC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC7D,IAAI,CAAC;gBACH,mCAAmC;gBACnC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;oBAAE,OAAM;gBAE/D,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,IAAI,GAAG,MAAM,eAAe,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;oBAExE,wBAAwB;oBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;wBAC7B,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAA;oBACnE,CAAC;oBAED,UAAU,GAAG,IAAI,CAAA;gBACnB,CAAC;gBAED,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;oBAAE,OAAM;gBAE9B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;;;;;EAKzB,UAAU;;;;;;;;;yBASa,CAAC,CAAA;YACpB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,CAAC,CAAA;YACzD,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentmap/opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "OpenCode plugin that injects agentmap codebase map into system prompt",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -15,20 +15,32 @@
|
|
|
15
15
|
"main": "./dist/index.js",
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
17
|
"exports": {
|
|
18
|
+
"./package.json": "./package.json",
|
|
18
19
|
".": {
|
|
19
20
|
"types": "./dist/index.d.ts",
|
|
20
21
|
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./src": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./src/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"./src/*": {
|
|
28
|
+
"types": "./dist/*.d.ts",
|
|
29
|
+
"default": "./src/*.ts"
|
|
21
30
|
}
|
|
22
31
|
},
|
|
23
32
|
"files": [
|
|
24
|
-
"
|
|
33
|
+
"src",
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"CHANGELOG.md"
|
|
25
37
|
],
|
|
26
38
|
"scripts": {
|
|
27
39
|
"build": "tsc",
|
|
28
40
|
"prepublishOnly": "bun run build"
|
|
29
41
|
},
|
|
30
42
|
"dependencies": {
|
|
31
|
-
"agentmap": "0.
|
|
43
|
+
"agentmap": "^0.8.0"
|
|
32
44
|
},
|
|
33
45
|
"devDependencies": {
|
|
34
46
|
"@opencode-ai/plugin": "^1.0.224",
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
|
|
2
|
+
// OpenCode plugin that injects codebase map into system prompt.
|
|
3
|
+
|
|
4
|
+
import type { Plugin } from '@opencode-ai/plugin'
|
|
5
|
+
import { formatLogMessage } from 'agentmap/src/logger'
|
|
6
|
+
import type { Logger } from 'agentmap/src/logger'
|
|
7
|
+
import { generateMapYaml } from 'agentmap/src/index'
|
|
8
|
+
|
|
9
|
+
const MAX_LINES = 1000
|
|
10
|
+
|
|
11
|
+
export const AgentMapPlugin: Plugin = async ({ directory, client }) => {
|
|
12
|
+
let cachedYaml: string | undefined
|
|
13
|
+
let lastSessionID: string | undefined
|
|
14
|
+
|
|
15
|
+
const logger: Logger = {
|
|
16
|
+
debug: () => {},
|
|
17
|
+
info: () => {},
|
|
18
|
+
warn: () => {},
|
|
19
|
+
error: (...args) => {
|
|
20
|
+
const message = formatLogMessage(args)
|
|
21
|
+
void client.tui.showToast({
|
|
22
|
+
body: {
|
|
23
|
+
title: 'agentmap',
|
|
24
|
+
message,
|
|
25
|
+
variant: 'error',
|
|
26
|
+
},
|
|
27
|
+
}).catch(() => {})
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
'chat.message': async ({ sessionID }) => {
|
|
33
|
+
if (sessionID !== lastSessionID) {
|
|
34
|
+
lastSessionID = sessionID
|
|
35
|
+
cachedYaml = undefined
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
'experimental.chat.system.transform': async (_input, output) => {
|
|
40
|
+
try {
|
|
41
|
+
// Skip if already has agentmap tag
|
|
42
|
+
if (output.system.some((s) => s.includes('<agentmap>'))) return
|
|
43
|
+
|
|
44
|
+
if (!cachedYaml) {
|
|
45
|
+
let yaml = await generateMapYaml({ dir: directory, diff: true, logger })
|
|
46
|
+
|
|
47
|
+
// Truncate to max lines
|
|
48
|
+
const lines = yaml.split('\n')
|
|
49
|
+
if (lines.length > MAX_LINES) {
|
|
50
|
+
yaml = lines.slice(0, MAX_LINES).join('\n') + '\n# ... truncated'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
cachedYaml = yaml
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!cachedYaml.trim()) return
|
|
57
|
+
|
|
58
|
+
output.system.push(`
|
|
59
|
+
|
|
60
|
+
<agentmap>
|
|
61
|
+
Tree of the most important files in the repo, showing descriptions and definitions:
|
|
62
|
+
|
|
63
|
+
${cachedYaml}
|
|
64
|
+
</agentmap>
|
|
65
|
+
|
|
66
|
+
<agentmap-instructions>
|
|
67
|
+
When creating new files, add a brief description comment at the top explaining the file's purpose. This makes the file discoverable in the agentmap.
|
|
68
|
+
|
|
69
|
+
When making significant changes to a file's purpose or responsibilities, update its header comment to reflect the changes.
|
|
70
|
+
|
|
71
|
+
These descriptions appear in the agentmap XML at the start of every agent session.
|
|
72
|
+
</agentmap-instructions>`)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
logger.error('[agentmap] Failed to generate map:', err)
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}
|