@plainconceptsplatform/agent-harness 3.4.1 → 3.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.
Files changed (96) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +455 -454
  3. package/cli/commands/join.js +259 -259
  4. package/cli/commands/migrate.js +310 -310
  5. package/cli/commands/shared.js +27 -27
  6. package/cli/commands/single.js +79 -79
  7. package/cli/commands/wizard.js +134 -134
  8. package/cli/fragments/ops-backlog/az.md +28 -28
  9. package/cli/fragments/ops-backlog/gh.md +28 -28
  10. package/cli/fragments/ops-backlog/jira.md +28 -28
  11. package/cli/fragments/ops-review/az.md +62 -62
  12. package/cli/fragments/ops-review/gh.md +51 -51
  13. package/cli/fragments/ops-review/gl.md +55 -55
  14. package/cli/fragments/ops-ship/gh.md +67 -67
  15. package/cli/fragments/ops-ship/gl.md +84 -84
  16. package/cli/index.js +116 -116
  17. package/cli/presets/clean.json +21 -21
  18. package/cli/presets/models.json +68 -68
  19. package/cli/presets/optimization.json +32 -32
  20. package/cli/presets/platforms.json +76 -76
  21. package/cli/presets/source.json +23 -23
  22. package/cli/steps/clean/index.js +120 -120
  23. package/cli/steps/copy/agents.js +120 -120
  24. package/cli/steps/copy/fullstack-engineer.js +89 -89
  25. package/cli/steps/copy/opencode-json.js +324 -324
  26. package/cli/steps/copy/skills.js +316 -316
  27. package/cli/steps/models/format.js +88 -88
  28. package/cli/steps/models/index.js +64 -64
  29. package/cli/steps/openspec/index.js +136 -136
  30. package/cli/steps/optimization/codegraph.js +141 -141
  31. package/cli/steps/optimization/detect.js +66 -66
  32. package/cli/steps/optimization/humanizer.js +17 -17
  33. package/cli/steps/optimization/index.js +158 -158
  34. package/cli/steps/optimization/memory.js +91 -91
  35. package/cli/steps/optimization/patch-guardrails.js +111 -111
  36. package/cli/steps/optimization/simple-english.js +17 -17
  37. package/cli/steps/optimization/skills-lock.js +30 -30
  38. package/cli/steps/platform/index.js +109 -109
  39. package/cli/steps/source/index.js +123 -123
  40. package/cli/utils/agent-color.js +111 -111
  41. package/cli/utils/copy.js +134 -134
  42. package/cli/utils/exec-spinner.js +47 -47
  43. package/cli/utils/exec.js +134 -134
  44. package/cli/utils/legacy-check.js +30 -30
  45. package/cli/utils/models-cache.js +58 -58
  46. package/cli/utils/models-pricing.js +42 -42
  47. package/cli/utils/paths.js +81 -81
  48. package/cli/utils/process.js +3 -3
  49. package/cli/utils/terminal.js +6 -6
  50. package/cli/utils/update-manifest.js +75 -75
  51. package/harness/.opencode/_gitignore +10 -10
  52. package/harness/.opencode/commands/ops-backlog.md +10 -10
  53. package/harness/.opencode/commands/ops-review.md +10 -10
  54. package/harness/.opencode/commands/ops-ship.md +9 -9
  55. package/harness/.opencode/commands/plan-archive.md +7 -7
  56. package/harness/.opencode/commands/plan-explore.md +11 -11
  57. package/harness/.opencode/commands/plan-propose.md +11 -11
  58. package/harness/.opencode/commands/plan-quick.md +7 -7
  59. package/harness/.opencode/commands/plan-story.md +11 -11
  60. package/harness/.opencode/commands/repo-audit.md +7 -7
  61. package/harness/.opencode/commands/repo-help.md +5 -5
  62. package/harness/.opencode/package.json +5 -5
  63. package/harness/.opencode/plugins/pc-commit-coauthor.js +41 -0
  64. package/harness/.opencode/plugins/pc-subagent-monitor.js +184 -184
  65. package/harness/.opencode/plugins/pc-subagent-tiers.js +367 -367
  66. package/harness/.opencode/plugins/pc-system-reminders.js +386 -386
  67. package/harness/.opencode/scripts/check-diff.mjs +252 -244
  68. package/harness/.opencode/scripts/code-rules.mjs +288 -288
  69. package/harness/ARCHITECTURE.md +13 -13
  70. package/harness/DESIGN.md +13 -13
  71. package/harness/opencode.jsonc +36 -36
  72. package/harness/openspec/config.yaml +20 -20
  73. package/harness/skills-lock.json +17 -17
  74. package/package.json +14 -12
  75. package/skills/agent-harness-cli/SKILL.md +86 -86
  76. package/skills/pc-guardrails-generic/SKILL.md +53 -53
  77. package/skills/pc-guardrails-project/SKILL.md +8 -8
  78. package/skills/pc-make-architecture/structure-template.md +64 -64
  79. package/skills/pc-make-engineer/SKILL.md +59 -59
  80. package/skills/pc-make-engineer/template.md +36 -36
  81. package/skills/pc-make-guardrails/category-reference.md +112 -112
  82. package/skills/pc-make-merge-risk-assess/SKILL.md +70 -70
  83. package/skills/pc-make-merge-risk-assess/category-reference.md +114 -114
  84. package/skills/pc-ops-ship/SKILL.md +22 -22
  85. package/skills/pc-plan-apply/SKILL.md +88 -86
  86. package/skills/pc-plan-apply/simple-mode.md +21 -21
  87. package/skills/pc-plan-explore/SKILL.md +21 -21
  88. package/skills/pc-plan-goal/SKILL.md +91 -91
  89. package/skills/pc-plan-goal/failure-policy.md +30 -30
  90. package/skills/pc-plan-goal/output-mode.md +10 -10
  91. package/skills/pc-plan-propose/SKILL.md +125 -125
  92. package/skills/pc-plan-propose/task-annotation.md +39 -39
  93. package/skills/pc-plan-quick/SKILL.md +46 -46
  94. package/skills/pc-repo-audit/SKILL.md +44 -44
  95. package/skills/pc-repo-help/SKILL.md +87 -87
  96. package/skills/pc-repo-verify/SKILL.md +150 -150
@@ -1,158 +1,158 @@
1
- import { checkbox, confirm } from '@inquirer/prompts'
2
- import { execa } from 'execa'
3
- import fse from 'fs-extra'
4
- import path from 'path'
5
- import { fileURLToPath } from 'url'
6
- import { code, commandExists, header, info, loading, success, warn } from '../../utils/exec.js'
7
- import { installSimpleEnglish } from './simple-english.js'
8
- import { installCodegraph } from './codegraph.js'
9
- import { installMemory } from './memory.js'
10
- import { installHumanizer } from './humanizer.js'
11
- import { patchGuardrails } from './patch-guardrails.js'
12
-
13
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
14
- const OPTIMIZATION_PRESET_PATH = path.resolve(__dirname, '../../presets/optimization.json')
15
- const optimizationPreset = await fse.readJson(OPTIMIZATION_PRESET_PATH)
16
-
17
- export async function checkRtk(options = {}) {
18
- if (!options.skipHeader) header('Checking rtk')
19
-
20
- let shouldCheck = true
21
- if (!options.skipPrompt) {
22
- info('Recommended: install and verify rtk for safer agent CLI command execution.')
23
- shouldCheck = await confirm({
24
- message: 'Check rtk now?',
25
- default: true,
26
- })
27
- }
28
-
29
- if (!shouldCheck) {
30
- warn('Skipped rtk check (you can install it later)')
31
- return { optedIn: false, checked: false, available: false }
32
- }
33
-
34
- loading('checking rtk...')
35
-
36
- const available = await commandExists('rtk')
37
-
38
- if (available) {
39
- success('rtk is available')
40
- return { optedIn: true, checked: true, available: true }
41
- }
42
-
43
- warn('rtk not found on PATH.')
44
- console.log()
45
- info('rtk is required for agents to run CLI commands safely.')
46
- info('Install it from: https://github.com/rtk-ai/rtk#pre-built-binaries')
47
- console.log()
48
- info('After installing, verify with:')
49
- code(['rtk --version'])
50
- return { optedIn: true, checked: true, available: false }
51
- }
52
-
53
- export async function tokenOptimizationStep(options = {}) {
54
- header('Step 8, Token optimization tools')
55
-
56
- const defaultSelected = optimizationPreset.choices
57
- .filter(choice => choice.checked)
58
- .map(choice => choice.value)
59
- let selected = defaultSelected
60
-
61
- if (!options.skipPrompt && process.stdin.isTTY) {
62
- info(optimizationPreset.info)
63
- // Abort the prompt via signal instead of racing it: a raced prompt keeps
64
- // stdin in raw mode after "losing" and rejects unhandled on a later Ctrl+C.
65
- const controller = new AbortController()
66
- const timer = setTimeout(() => controller.abort(), optimizationPreset.timeoutMs)
67
- try {
68
- selected = await checkbox(
69
- {
70
- message: optimizationPreset.message,
71
- choices: optimizationPreset.choices,
72
- },
73
- { signal: controller.signal },
74
- )
75
- } catch (err) {
76
- if (err.name !== 'AbortPromptError') throw err
77
- selected = defaultSelected
78
- info('No response: continuing with the recommended defaults.')
79
- } finally {
80
- clearTimeout(timer)
81
- }
82
- }
83
-
84
- loading('applying token optimization selections...')
85
-
86
- const installScope = options.ctx?.installScope || 'local'
87
-
88
- const has = value => selected.includes(value)
89
-
90
- const rtk = has('rtk')
91
- ? await checkRtk({ skipHeader: true, skipPrompt: true })
92
- : { optedIn: false, checked: false, available: false }
93
-
94
- const simpleEnglish = has('simpleEnglish')
95
- ? await installSimpleEnglish({
96
- skipHeader: true,
97
- skipPrompt: true,
98
- installScope,
99
- })
100
- : { optedIn: false, installed: false }
101
-
102
- const codegraph = has('codegraph')
103
- ? await installCodegraph({ skipHeader: true, installScope })
104
- : { optedIn: false, installed: false }
105
-
106
- const memory = has('memory')
107
- ? await installMemory({ skipHeader: true })
108
- : { optedIn: false, installed: false }
109
-
110
- const humanizer = has('humanizer')
111
- ? await installHumanizer({ skipHeader: true, installScope })
112
- : { optedIn: false, installed: false }
113
-
114
- // Run a single npx skills experimental_install to pick up all skills
115
- // that were added to skills-lock.json by the individual installers above.
116
- await syncSkillsLock()
117
-
118
- // Patch guardrails skill with selected tool guidance sections
119
- await patchGuardrails({
120
- rtk: rtk.available,
121
- codegraph: codegraph.optedIn,
122
- memory: memory.optedIn,
123
- simpleEnglish: simpleEnglish.installed,
124
- humanizer: humanizer.optedIn,
125
- })
126
-
127
- if (selected.length === 0) warn('No token optimization tools selected')
128
- else success('Token optimization step completed')
129
-
130
- return { rtk, simpleEnglish, codegraph, memory, humanizer }
131
- }
132
-
133
- async function syncSkillsLock() {
134
- const destLock = path.join(process.cwd(), 'skills-lock.json')
135
- if (!await fse.pathExists(destLock)) {
136
- info('No skills-lock.json found, skipping skills sync')
137
- return
138
- }
139
-
140
- loading('syncing skills via npx skills experimental_install...')
141
- try {
142
- const result = await execa('npx', ['skills', 'experimental_install', '--yes'], {
143
- cwd: process.cwd(),
144
- reject: false,
145
- stdio: 'pipe',
146
- timeout: 600000,
147
- })
148
-
149
- if (result.exitCode === 0) {
150
- success('Skills synced from skills-lock.json')
151
- } else {
152
- const errLine = result.stderr?.trim().split('\n').slice(-3).join('\n')
153
- warn(`npx skills experimental_install exited with non-zero code${errLine ? `: ${errLine}` : ''}`)
154
- }
155
- } catch (err) {
156
- warn(`npx skills experimental_install failed: ${err.message}`)
157
- }
158
- }
1
+ import { checkbox, confirm } from '@inquirer/prompts'
2
+ import { execa } from 'execa'
3
+ import fse from 'fs-extra'
4
+ import path from 'path'
5
+ import { fileURLToPath } from 'url'
6
+ import { code, commandExists, header, info, loading, success, warn } from '../../utils/exec.js'
7
+ import { installSimpleEnglish } from './simple-english.js'
8
+ import { installCodegraph } from './codegraph.js'
9
+ import { installMemory } from './memory.js'
10
+ import { installHumanizer } from './humanizer.js'
11
+ import { patchGuardrails } from './patch-guardrails.js'
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
14
+ const OPTIMIZATION_PRESET_PATH = path.resolve(__dirname, '../../presets/optimization.json')
15
+ const optimizationPreset = await fse.readJson(OPTIMIZATION_PRESET_PATH)
16
+
17
+ export async function checkRtk(options = {}) {
18
+ if (!options.skipHeader) header('Checking rtk')
19
+
20
+ let shouldCheck = true
21
+ if (!options.skipPrompt) {
22
+ info('Recommended: install and verify rtk for safer agent CLI command execution.')
23
+ shouldCheck = await confirm({
24
+ message: 'Check rtk now?',
25
+ default: true,
26
+ })
27
+ }
28
+
29
+ if (!shouldCheck) {
30
+ warn('Skipped rtk check (you can install it later)')
31
+ return { optedIn: false, checked: false, available: false }
32
+ }
33
+
34
+ loading('checking rtk...')
35
+
36
+ const available = await commandExists('rtk')
37
+
38
+ if (available) {
39
+ success('rtk is available')
40
+ return { optedIn: true, checked: true, available: true }
41
+ }
42
+
43
+ warn('rtk not found on PATH.')
44
+ console.log()
45
+ info('rtk is required for agents to run CLI commands safely.')
46
+ info('Install it from: https://github.com/rtk-ai/rtk#pre-built-binaries')
47
+ console.log()
48
+ info('After installing, verify with:')
49
+ code(['rtk --version'])
50
+ return { optedIn: true, checked: true, available: false }
51
+ }
52
+
53
+ export async function tokenOptimizationStep(options = {}) {
54
+ header('Step 8, Token optimization tools')
55
+
56
+ const defaultSelected = optimizationPreset.choices
57
+ .filter(choice => choice.checked)
58
+ .map(choice => choice.value)
59
+ let selected = defaultSelected
60
+
61
+ if (!options.skipPrompt && process.stdin.isTTY) {
62
+ info(optimizationPreset.info)
63
+ // Abort the prompt via signal instead of racing it: a raced prompt keeps
64
+ // stdin in raw mode after "losing" and rejects unhandled on a later Ctrl+C.
65
+ const controller = new AbortController()
66
+ const timer = setTimeout(() => controller.abort(), optimizationPreset.timeoutMs)
67
+ try {
68
+ selected = await checkbox(
69
+ {
70
+ message: optimizationPreset.message,
71
+ choices: optimizationPreset.choices,
72
+ },
73
+ { signal: controller.signal },
74
+ )
75
+ } catch (err) {
76
+ if (err.name !== 'AbortPromptError') throw err
77
+ selected = defaultSelected
78
+ info('No response: continuing with the recommended defaults.')
79
+ } finally {
80
+ clearTimeout(timer)
81
+ }
82
+ }
83
+
84
+ loading('applying token optimization selections...')
85
+
86
+ const installScope = options.ctx?.installScope || 'local'
87
+
88
+ const has = value => selected.includes(value)
89
+
90
+ const rtk = has('rtk')
91
+ ? await checkRtk({ skipHeader: true, skipPrompt: true })
92
+ : { optedIn: false, checked: false, available: false }
93
+
94
+ const simpleEnglish = has('simpleEnglish')
95
+ ? await installSimpleEnglish({
96
+ skipHeader: true,
97
+ skipPrompt: true,
98
+ installScope,
99
+ })
100
+ : { optedIn: false, installed: false }
101
+
102
+ const codegraph = has('codegraph')
103
+ ? await installCodegraph({ skipHeader: true, installScope })
104
+ : { optedIn: false, installed: false }
105
+
106
+ const memory = has('memory')
107
+ ? await installMemory({ skipHeader: true })
108
+ : { optedIn: false, installed: false }
109
+
110
+ const humanizer = has('humanizer')
111
+ ? await installHumanizer({ skipHeader: true, installScope })
112
+ : { optedIn: false, installed: false }
113
+
114
+ // Run a single npx skills experimental_install to pick up all skills
115
+ // that were added to skills-lock.json by the individual installers above.
116
+ await syncSkillsLock()
117
+
118
+ // Patch guardrails skill with selected tool guidance sections
119
+ await patchGuardrails({
120
+ rtk: rtk.available,
121
+ codegraph: codegraph.optedIn,
122
+ memory: memory.optedIn,
123
+ simpleEnglish: simpleEnglish.installed,
124
+ humanizer: humanizer.optedIn,
125
+ })
126
+
127
+ if (selected.length === 0) warn('No token optimization tools selected')
128
+ else success('Token optimization step completed')
129
+
130
+ return { rtk, simpleEnglish, codegraph, memory, humanizer }
131
+ }
132
+
133
+ async function syncSkillsLock() {
134
+ const destLock = path.join(process.cwd(), 'skills-lock.json')
135
+ if (!await fse.pathExists(destLock)) {
136
+ info('No skills-lock.json found, skipping skills sync')
137
+ return
138
+ }
139
+
140
+ loading('syncing skills via npx skills experimental_install...')
141
+ try {
142
+ const result = await execa('npx', ['skills', 'experimental_install', '--yes'], {
143
+ cwd: process.cwd(),
144
+ reject: false,
145
+ stdio: 'pipe',
146
+ timeout: 600000,
147
+ })
148
+
149
+ if (result.exitCode === 0) {
150
+ success('Skills synced from skills-lock.json')
151
+ } else {
152
+ const errLine = result.stderr?.trim().split('\n').slice(-3).join('\n')
153
+ warn(`npx skills experimental_install exited with non-zero code${errLine ? `: ${errLine}` : ''}`)
154
+ }
155
+ } catch (err) {
156
+ warn(`npx skills experimental_install failed: ${err.message}`)
157
+ }
158
+ }
@@ -1,91 +1,91 @@
1
- import { execa } from 'execa'
2
- import { addSkillToLock } from './skills-lock.js'
3
- import fse from 'fs-extra'
4
- import path from 'node:path'
5
- import { applyEdits, modify, parse as parseJsonc } from 'jsonc-parser'
6
- import { error, info, loading, success, warn } from '../../utils/exec.js'
7
-
8
- /**
9
- * Configures the agentmemory MCP server in opencode.jsonc
10
- * and installs the agentmemory skills.
11
- *
12
- * agentmemory runs as a persistent local server on localhost:3111.
13
- * Requires Node.js 18+ (npm install -g @agentmemory/agentmemory).
14
- * MCP bridge: npx -y @agentmemory/mcp (proxies to the running server).
15
- * Skill source: https://github.com/rohitg00/agentmemory
16
- */
17
- export async function installMemory(options = {}) {
18
- if (!options.skipHeader) info('Configuring agentmemory local memory server...')
19
-
20
- // Install agentmemory globally so the `agentmemory` command is on PATH
21
- loading('installing agentmemory (this may take a minute)...')
22
- try {
23
- const installResult = await execa('npm', ['install', '-g', '@agentmemory/agentmemory'], {
24
- reject: false,
25
- timeout: 300000,
26
- stdio: 'pipe',
27
- })
28
- if (installResult.exitCode === 0) {
29
- success('agentmemory installed')
30
- } else {
31
- warn('agentmemory install exited with non-zero code, MCP may fall back to npx resolution')
32
- }
33
- } catch (err) {
34
- warn(`agentmemory install failed: ${err.message}`)
35
- }
36
-
37
- // Configure MCP server in opencode.jsonc
38
- try {
39
- const opencodePath = path.join(process.cwd(), 'opencode.jsonc')
40
-
41
- let opencodeText = JSON.stringify({ $schema: 'https://opencode.ai/config.json' }, null, 2)
42
- let opencode
43
- if (await fse.pathExists(opencodePath)) {
44
- const errors = []
45
- opencodeText = await fse.readFile(opencodePath, 'utf-8')
46
- opencode = parseJsonc(opencodeText, errors)
47
- if (errors.length > 0 || !opencode || typeof opencode !== 'object' || Array.isArray(opencode)) {
48
- throw new Error('opencode.jsonc could not be parsed')
49
- }
50
- } else {
51
- opencode = { $schema: 'https://opencode.ai/config.json' }
52
- }
53
-
54
- if (!opencode.mcp?.servers?.agentmemory) {
55
- const agentmemory = {
56
- type: 'local',
57
- command: ['npx', '-y', '@agentmemory/mcp'],
58
- environment: {
59
- AGENTMEMORY_URL: 'http://localhost:3111',
60
- },
61
- disabled: false,
62
- timeout: {
63
- "catalog": 120000,
64
- "execution": 120000
65
- }
66
- }
67
- opencodeText = applyEdits(opencodeText, modify(opencodeText, ['mcp', 'servers', 'agentmemory'], agentmemory, {
68
- formattingOptions: { insertSpaces: true, tabSize: 2 },
69
- }))
70
- }
71
-
72
- await fse.writeFile(opencodePath, opencodeText, 'utf-8')
73
- success('agentmemory MCP server configured in opencode.jsonc')
74
- } catch (err) {
75
- error(`Failed to configure agentmemory MCP: ${err.message}`)
76
- return { optedIn: true, installed: false }
77
- }
78
-
79
- // Add agentmemory skills to skills-lock.json for batch install
80
- await addSkillToLock('agentmemory-agents', {
81
- source: 'rohitg00/agentmemory',
82
- sourceType: 'github',
83
- skillPath: 'plugin/skills/agentmemory-agents/SKILL.md',
84
- })
85
-
86
- info('Memory server: run `agentmemory` to start on localhost:3111')
87
- info('Real-time viewer: http://localhost:3113')
88
- info('Health check: curl http://localhost:3111/agentmemory/health')
89
-
90
- return { optedIn: true, installed: true }
91
- }
1
+ import { execa } from 'execa'
2
+ import { addSkillToLock } from './skills-lock.js'
3
+ import fse from 'fs-extra'
4
+ import path from 'node:path'
5
+ import { applyEdits, modify, parse as parseJsonc } from 'jsonc-parser'
6
+ import { error, info, loading, success, warn } from '../../utils/exec.js'
7
+
8
+ /**
9
+ * Configures the agentmemory MCP server in opencode.jsonc
10
+ * and installs the agentmemory skills.
11
+ *
12
+ * agentmemory runs as a persistent local server on localhost:3111.
13
+ * Requires Node.js 18+ (npm install -g @agentmemory/agentmemory).
14
+ * MCP bridge: npx -y @agentmemory/mcp (proxies to the running server).
15
+ * Skill source: https://github.com/rohitg00/agentmemory
16
+ */
17
+ export async function installMemory(options = {}) {
18
+ if (!options.skipHeader) info('Configuring agentmemory local memory server...')
19
+
20
+ // Install agentmemory globally so the `agentmemory` command is on PATH
21
+ loading('installing agentmemory (this may take a minute)...')
22
+ try {
23
+ const installResult = await execa('npm', ['install', '-g', '@agentmemory/agentmemory'], {
24
+ reject: false,
25
+ timeout: 300000,
26
+ stdio: 'pipe',
27
+ })
28
+ if (installResult.exitCode === 0) {
29
+ success('agentmemory installed')
30
+ } else {
31
+ warn('agentmemory install exited with non-zero code, MCP may fall back to npx resolution')
32
+ }
33
+ } catch (err) {
34
+ warn(`agentmemory install failed: ${err.message}`)
35
+ }
36
+
37
+ // Configure MCP server in opencode.jsonc
38
+ try {
39
+ const opencodePath = path.join(process.cwd(), 'opencode.jsonc')
40
+
41
+ let opencodeText = JSON.stringify({ $schema: 'https://opencode.ai/config.json' }, null, 2)
42
+ let opencode
43
+ if (await fse.pathExists(opencodePath)) {
44
+ const errors = []
45
+ opencodeText = await fse.readFile(opencodePath, 'utf-8')
46
+ opencode = parseJsonc(opencodeText, errors)
47
+ if (errors.length > 0 || !opencode || typeof opencode !== 'object' || Array.isArray(opencode)) {
48
+ throw new Error('opencode.jsonc could not be parsed')
49
+ }
50
+ } else {
51
+ opencode = { $schema: 'https://opencode.ai/config.json' }
52
+ }
53
+
54
+ if (!opencode.mcp?.servers?.agentmemory) {
55
+ const agentmemory = {
56
+ type: 'local',
57
+ command: ['npx', '-y', '@agentmemory/mcp'],
58
+ environment: {
59
+ AGENTMEMORY_URL: 'http://localhost:3111',
60
+ },
61
+ disabled: false,
62
+ timeout: {
63
+ "catalog": 120000,
64
+ "execution": 120000
65
+ }
66
+ }
67
+ opencodeText = applyEdits(opencodeText, modify(opencodeText, ['mcp', 'servers', 'agentmemory'], agentmemory, {
68
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
69
+ }))
70
+ }
71
+
72
+ await fse.writeFile(opencodePath, opencodeText, 'utf-8')
73
+ success('agentmemory MCP server configured in opencode.jsonc')
74
+ } catch (err) {
75
+ error(`Failed to configure agentmemory MCP: ${err.message}`)
76
+ return { optedIn: true, installed: false }
77
+ }
78
+
79
+ // Add agentmemory skills to skills-lock.json for batch install
80
+ await addSkillToLock('agentmemory-agents', {
81
+ source: 'rohitg00/agentmemory',
82
+ sourceType: 'github',
83
+ skillPath: 'plugin/skills/agentmemory-agents/SKILL.md',
84
+ })
85
+
86
+ info('Memory server: run `agentmemory` to start on localhost:3111')
87
+ info('Real-time viewer: http://localhost:3113')
88
+ info('Health check: curl http://localhost:3111/agentmemory/health')
89
+
90
+ return { optedIn: true, installed: true }
91
+ }