@plainconceptsplatform/agent-harness 2.0.0 → 2.0.1

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 (116) hide show
  1. package/README.md +423 -419
  2. package/package.json +3 -3
  3. package/src/commands/join.js +244 -244
  4. package/src/commands/shared.js +27 -27
  5. package/src/commands/single.js +79 -79
  6. package/src/commands/update.js +109 -109
  7. package/src/commands/wizard.js +134 -134
  8. package/src/content/.agents/skills/browser-automation/SKILL.md +66 -66
  9. package/src/content/.agents/skills/pc-guardrails-generic/SKILL.md +68 -68
  10. package/src/content/.agents/skills/pc-guardrails-project/SKILL.md +8 -8
  11. package/src/content/.agents/skills/pc-make-architecture/SKILL.md +51 -51
  12. package/src/content/.agents/skills/pc-make-architecture/structure-template.md +38 -38
  13. package/src/content/.agents/skills/pc-make-design/SKILL.md +68 -68
  14. package/src/content/.agents/skills/pc-make-engineer/SKILL.md +219 -219
  15. package/src/content/.agents/skills/pc-make-engineer/signal-mapping.md +68 -68
  16. package/src/content/.agents/skills/pc-make-engineer/template.md +81 -81
  17. package/src/content/.agents/skills/pc-make-evidence-scaffold/SKILL.md +18 -18
  18. package/src/content/.agents/skills/pc-make-evidence-scaffold/evidence-contract.md +29 -29
  19. package/src/content/.agents/skills/pc-make-guardrails/SKILL.md +74 -74
  20. package/src/content/.agents/skills/pc-make-guardrails/category-reference.md +68 -68
  21. package/src/content/.agents/skills/pc-make-merge-risk-assess/SKILL.md +70 -70
  22. package/src/content/.agents/skills/pc-make-merge-risk-assess/category-reference.md +98 -98
  23. package/src/content/.agents/skills/pc-make-user-model/SKILL.md +66 -66
  24. package/src/content/.agents/skills/pc-ops-evidence/SKILL.md +127 -127
  25. package/src/content/.agents/skills/pc-ops-ship/SKILL.md +18 -18
  26. package/src/content/.agents/skills/pc-plan-apply/SKILL.md +83 -83
  27. package/src/content/.agents/skills/pc-plan-apply/simple-mode.md +21 -21
  28. package/src/content/.agents/skills/pc-plan-archive/SKILL.md +63 -63
  29. package/src/content/.agents/skills/pc-plan-explore/SKILL.md +9 -9
  30. package/src/content/.agents/skills/pc-plan-goal/SKILL.md +94 -94
  31. package/src/content/.agents/skills/pc-plan-goal/branching.md +30 -30
  32. package/src/content/.agents/skills/pc-plan-goal/failure-policy.md +30 -30
  33. package/src/content/.agents/skills/pc-plan-goal/output-mode.md +9 -9
  34. package/src/content/.agents/skills/pc-plan-goal/output.md +68 -68
  35. package/src/content/.agents/skills/pc-plan-propose/SKILL.md +125 -125
  36. package/src/content/.agents/skills/pc-plan-propose/task-annotation.md +39 -39
  37. package/src/content/.agents/skills/pc-plan-quick/SKILL.md +62 -62
  38. package/src/content/.agents/skills/pc-plan-story/SKILL.md +146 -146
  39. package/src/content/.agents/skills/pc-repo-audit/SKILL.md +44 -44
  40. package/src/content/.agents/skills/pc-repo-help/SKILL.md +91 -91
  41. package/src/content/.agents/skills/pc-repo-initialize/SKILL.md +130 -130
  42. package/src/content/.agents/skills/pc-repo-onboard/SKILL.md +87 -87
  43. package/src/content/.agents/skills/pc-repo-verify/SKILL.md +34 -34
  44. package/src/content/.agents/skills/pc-userstory-az/SKILL.md +157 -157
  45. package/src/content/.agents/skills/pc-userstory-browser/SKILL.md +132 -132
  46. package/src/content/.agents/skills/pc-userstory-gh/SKILL.md +120 -120
  47. package/src/content/.agents/skills/pc-userstory-jira/SKILL.md +131 -131
  48. package/src/content/.opencode/_gitignore +7 -7
  49. package/src/content/.opencode/commands/init.md +5 -5
  50. package/src/content/.opencode/commands/make-architecture.md +5 -5
  51. package/src/content/.opencode/commands/make-design.md +5 -5
  52. package/src/content/.opencode/commands/make-engineer.md +5 -5
  53. package/src/content/.opencode/commands/make-evidence-scaffold.md +5 -5
  54. package/src/content/.opencode/commands/make-guardrails.md +5 -5
  55. package/src/content/.opencode/commands/make-user-model.md +5 -5
  56. package/src/content/.opencode/commands/ops-backlog.md +10 -10
  57. package/src/content/.opencode/commands/ops-evidence.md +9 -9
  58. package/src/content/.opencode/commands/ops-review.md +8 -8
  59. package/src/content/.opencode/commands/ops-ship.md +9 -9
  60. package/src/content/.opencode/commands/plan-apply.md +9 -9
  61. package/src/content/.opencode/commands/plan-archive.md +5 -5
  62. package/src/content/.opencode/commands/plan-explore.md +9 -9
  63. package/src/content/.opencode/commands/plan-goal.md +5 -5
  64. package/src/content/.opencode/commands/plan-propose.md +9 -9
  65. package/src/content/.opencode/commands/plan-quick.md +5 -5
  66. package/src/content/.opencode/commands/plan-story.md +9 -9
  67. package/src/content/.opencode/commands/repo-audit.md +5 -5
  68. package/src/content/.opencode/commands/repo-help.md +5 -5
  69. package/src/content/.opencode/commands/repo-initialize.md +5 -5
  70. package/src/content/.opencode/commands/repo-onboard.md +5 -5
  71. package/src/content/.opencode/commands/repo-verify.md +5 -5
  72. package/src/content/.opencode/plugins/pc-subagent-monitor.js +139 -139
  73. package/src/content/.opencode/plugins/pc-subagent-tiers.js +179 -179
  74. package/src/content/.opencode/plugins/pc-system-reminders.js +96 -96
  75. package/src/content/.opencode/tui/pc-subagents.tsx +98 -98
  76. package/src/content/.opencode/tui.json +6 -6
  77. package/src/content/AGENTS.md +71 -71
  78. package/src/fragments/archive/az.md +95 -95
  79. package/src/fragments/archive/gh.md +94 -94
  80. package/src/fragments/archive/gl.md +94 -94
  81. package/src/fragments/archive/none.md +73 -73
  82. package/src/fragments/guardrails/codegraph.md +7 -7
  83. package/src/fragments/guardrails/humanizer.md +4 -4
  84. package/src/fragments/guardrails/memory.md +4 -4
  85. package/src/fragments/guardrails/rtk.md +3 -3
  86. package/src/fragments/guardrails/simple-english.md +4 -4
  87. package/src/fragments/ops-backlog/az.md +28 -28
  88. package/src/fragments/ops-backlog/gh.md +29 -29
  89. package/src/fragments/ops-backlog/jira.md +28 -28
  90. package/src/fragments/ops-evidence/az.md +41 -41
  91. package/src/fragments/ops-evidence/gh.md +53 -53
  92. package/src/fragments/ops-evidence/jira.md +38 -38
  93. package/src/fragments/ops-review/az.md +62 -62
  94. package/src/fragments/ops-review/gh.md +52 -52
  95. package/src/fragments/ops-review/gl.md +56 -56
  96. package/src/fragments/ops-ship/az.md +80 -80
  97. package/src/fragments/ops-ship/gh.md +68 -68
  98. package/src/fragments/ops-ship/gl.md +85 -85
  99. package/src/index.js +107 -107
  100. package/src/presets/agents-content.json +53 -53
  101. package/src/presets/models.json +68 -68
  102. package/src/steps/copy/agents.js +118 -118
  103. package/src/steps/copy/commands.js +91 -91
  104. package/src/steps/copy/fullstack-engineer.js +83 -83
  105. package/src/steps/copy/index.js +88 -88
  106. package/src/steps/copy/opencode-json.js +129 -129
  107. package/src/steps/copy/skills.js +196 -196
  108. package/src/steps/metadata/index.js +108 -108
  109. package/src/steps/models/write.js +34 -34
  110. package/src/steps/optimization/patch-guardrails.js +108 -108
  111. package/src/utils/copy.js +108 -108
  112. package/src/utils/legacy-check.js +30 -30
  113. package/src/utils/models-cache.js +58 -58
  114. package/src/utils/paths.js +64 -64
  115. package/src/utils/update-manifest.js +49 -49
  116. package/src/content/.opencode/plugins/pc-system-reminders.test.js +0 -35
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@plainconceptsplatform/agent-harness",
3
- "version": "2.0.0",
4
- "description": "Installs and maintains an agent harness in any codebase. Wires OpenCode, OpenSpec, codegraph, and agentmemory into a multi-agent development workflow powered by native parallel subagents.",
3
+ "version": "2.0.1",
4
+ "description": "Installs the Plain Concepts Platform Harness into any codebase, and keeps it up to date. Wires OpenCode, OpenSpec, codegraph, and agentmemory into a multi-agent workflow that runs on native parallel subagents.",
5
5
  "keywords": [
6
6
  "opencode",
7
7
  "ai",
@@ -28,9 +28,9 @@
28
28
  },
29
29
  "files": [
30
30
  "src",
31
+ "src/content",
31
32
  "!src/**/*.test.js",
32
33
  "!src/**/__tests__",
33
- "src/content",
34
34
  "!src/content/**/node_modules"
35
35
  ],
36
36
  "publishConfig": {
@@ -1,244 +1,244 @@
1
- import chalk from 'chalk'
2
- import { execa } from 'execa'
3
- import fse from 'fs-extra'
4
- import path from 'node:path'
5
- import { parse as parseJsonc } from 'jsonc-parser'
6
- import { installBrowser } from '../steps/browser/index.js'
7
- import { checkRtk } from '../steps/optimization/index.js'
8
- import { checkPlatform } from '../steps/platform/index.js'
9
- import { commandExists, header, info, loading, success, warn } from '../utils/exec.js'
10
- import { IGNORED_ENTRIES, USER_CONFIG_FILE } from '../utils/paths.js'
11
- import { readHarnessConfig } from './shared.js'
12
-
13
- export async function runJoin() {
14
- const logo = chalk.hex('#fe3d57')
15
- console.log()
16
- console.log(logo(' 🤝 agent-harness join'))
17
- console.log(chalk.dim(' New team member setup: checks & local installs only.'))
18
- console.log(chalk.dim(' Does not modify committed project files.'))
19
- console.log()
20
-
21
- const saved = await readHarnessConfig()
22
-
23
- if (!saved) {
24
- warn('No .opencode/harness.json found.')
25
- warn('This project may not have been onboarded yet. Run `npx @plainconceptsplatform/agent-harness` first.')
26
- return
27
- }
28
-
29
- const backlogPlatform = saved?.platform?.backlog
30
- const repoPlatform = saved?.platform?.repo
31
- const installScope = 'local'
32
- const teamModels = saved?.models ?? {}
33
-
34
- const opencodeDir = path.join(process.cwd(), '.opencode')
35
- const opencodeJsonPath = path.join(process.cwd(), 'opencode.jsonc')
36
-
37
- // Step 1: Platform CLI check
38
- header('Step 1, Platform CLI check')
39
- const platformsToCheck = [...new Set([backlogPlatform, repoPlatform])].filter(p => p && p !== 'none')
40
- if (platformsToCheck.length === 0) {
41
- info('No platform integration selected, skipping CLI checks.')
42
- } else {
43
- for (const p of platformsToCheck) {
44
- const display = p === 'github' ? 'GitHub' : p === 'azure' ? 'Azure DevOps' : p
45
- info(`Checking platform: ${display}`)
46
- await checkPlatform(p)
47
- }
48
- }
49
-
50
- // Step 2: Install OpenCode plugins
51
- header('Step 2, Installing OpenCode plugins')
52
- const pkgPath = path.join(opencodeDir, 'package.json')
53
- if (await fse.pathExists(pkgPath)) {
54
- try {
55
- await execa('npm', ['install'], { cwd: opencodeDir, reject: false, stdio: 'pipe' })
56
- success('OpenCode plugins installed')
57
- } catch {
58
- warn('npm install failed in .opencode/: plugins may not load correctly')
59
- }
60
- } else {
61
- warn('No .opencode/package.json found: skipping plugin install')
62
- }
63
-
64
- // Step 3: Install/update skills
65
- header('Step 3, Installing skills')
66
- try {
67
- await execa('npx', ['skills'], { cwd: process.cwd(), reject: false, stdio: 'pipe' })
68
- success('Skills installed/updated')
69
- } catch {
70
- warn('npx skills failed: skills may be missing')
71
- }
72
-
73
- // Step 4: OpenSpec CLI check
74
- header('Step 4, Checking OpenSpec')
75
- const openspecAvailable = await commandExists('openspec')
76
- if (openspecAvailable) {
77
- success('OpenSpec CLI is available')
78
- } else {
79
- info('OpenSpec not found on PATH: installing...')
80
- try {
81
- const result = await execa('npm', ['install', '@fission-ai/openspec', '--global'], {
82
- cwd: process.cwd(),
83
- reject: false,
84
- stdio: 'pipe',
85
- })
86
- if (result.exitCode === 0) {
87
- success('OpenSpec installed')
88
- } else {
89
- warn('OpenSpec install failed: run `npm install -g @fission-ai/openspec` manually')
90
- }
91
- } catch {
92
- warn('OpenSpec install failed: run `npm install -g @fission-ai/openspec` manually')
93
- }
94
- }
95
-
96
- // Step 5: agentmemory (if project uses it)
97
- header('Step 5, Checking agentmemory')
98
- let cfg = {}
99
- if (await fse.pathExists(opencodeJsonPath)) {
100
- try {
101
- const errors = []
102
- cfg = parseJsonc(await fse.readFile(opencodeJsonPath, 'utf-8'), errors)
103
- if (errors.length > 0) cfg = {}
104
- } catch {
105
- // ignore config read errors
106
- }
107
- }
108
-
109
- if (cfg?.mcp?.['agentmemory']) {
110
- info('Project uses agentmemory MCP')
111
- loading('checking agentmemory server...')
112
-
113
- // Check if agentmemory is installed
114
- const agentmemoryAvailable = await commandExists('agentmemory')
115
- if (agentmemoryAvailable) {
116
- success('agentmemory CLI is available')
117
- } else {
118
- info('agentmemory not found on PATH, installing...')
119
- try {
120
- const installResult = await execa('npm', ['install', '-g', '@agentmemory/agentmemory'], {
121
- reject: false,
122
- timeout: 300000,
123
- stdio: 'pipe',
124
- })
125
- if (installResult.exitCode === 0) {
126
- success('agentmemory installed')
127
- } else {
128
- warn('agentmemory install failed, run `npm install -g @agentmemory/agentmemory` manually')
129
- }
130
- } catch (err) {
131
- warn(`agentmemory install failed: ${err.message}`)
132
- }
133
- }
134
-
135
- // Health-check the server
136
- try {
137
- const healthResult = await execa('curl', ['-s', '-o', '/dev/null', '-w', '%{http_code}', 'http://localhost:3111/agentmemory/health'], {
138
- reject: false,
139
- timeout: 5000,
140
- })
141
- if (healthResult.stdout.trim() === '200') {
142
- success('agentmemory server is running')
143
- } else {
144
- info('agentmemory server not running, start it with `agentmemory` in a separate terminal')
145
- }
146
- } catch {
147
- info('agentmemory server not running, start it with `agentmemory` in a separate terminal')
148
- }
149
- } else {
150
- info('agentmemory not configured for this project, skipping')
151
- }
152
-
153
- // Step 6: Codegraph (if project uses it)
154
- header('Step 6, Checking codegraph')
155
- if (cfg?.mcp?.['codegraph']) {
156
- info('Project uses codegraph MCP: building index...')
157
- try {
158
- const result = await execa('npx', ['--yes', '@colbymchenry/codegraph', 'init', '-i'], {
159
- cwd: process.cwd(),
160
- reject: false,
161
- stdio: 'pipe',
162
- timeout: 120000,
163
- })
164
- if (result.exitCode === 0) {
165
- success('codegraph index initialized')
166
- } else {
167
- warn('codegraph init failed: codegraph_search may be slow or unavailable')
168
- }
169
- } catch {
170
- warn('codegraph init failed: codegraph_search may be slow or unavailable')
171
- }
172
- } else {
173
- info('codegraph not configured for this project: skipping')
174
- }
175
-
176
- // Step 7: RTK check
177
- header('Step 7, Checking rtk')
178
- await checkRtk({ skipHeader: true, skipPrompt: true })
179
-
180
- // Step 8: Browser extension
181
- header('Step 8, Installing opencode-browser')
182
- await installBrowser({ installScope })
183
-
184
- // Step 9: Model tier guidance
185
- header('Step 9, Model tiers')
186
- const userConfigPath = path.join(opencodeDir, USER_CONFIG_FILE)
187
- const hasUserOverride = await fse.pathExists(userConfigPath)
188
-
189
- if (teamModels && Object.keys(teamModels).length > 0) {
190
- info('Team model configuration:')
191
- for (const [tier, model] of Object.entries(teamModels)) {
192
- info(` ${tier}: ${model}`)
193
- }
194
- console.log()
195
-
196
- if (hasUserOverride) {
197
- info('You have a local model override (harness.user.json).')
198
- info('To change: /make-user-model user <tier> <model>')
199
- } else {
200
- info('No local override: using team defaults.')
201
- info('To override a tier for your machine: /make-user-model user <tier> <model>')
202
- info(' e.g. /make-user-model user build current')
203
- }
204
- console.log()
205
- info('The pc-subagent-tiers plugin will generate *-engineer.<tier>.md variants')
206
- info('on opencode startup from these model configs.')
207
- } else {
208
- warn('No model tiers configured in harness.json.')
209
- warn('Run /make-user-model <tier> <model> for plan, build, and fast.')
210
- }
211
-
212
- // Step 10: Ensure .opencode/.gitignore exists
213
- header('Step 10, Checking .opencode/.gitignore')
214
- const gitignorePath = path.join(opencodeDir, '.gitignore')
215
- const requiredEntries = IGNORED_ENTRIES
216
-
217
- if (await fse.pathExists(gitignorePath)) {
218
- const content = await fse.readFile(gitignorePath, 'utf-8')
219
- const existing = content.split('\n').map(l => l.trim()).filter(Boolean)
220
- const missing = requiredEntries.filter(e => !existing.includes(e))
221
- if (missing.length > 0) {
222
- const merged = `${[...existing, ...missing].join('\n')}\n`
223
- await fse.writeFile(gitignorePath, merged, 'utf-8')
224
- success(`Merged ${missing.length} missing .gitignore entries`)
225
- } else {
226
- success('.gitignore is up to date')
227
- }
228
- } else {
229
- await fse.writeFile(gitignorePath, `${requiredEntries.join('\n')}\n`, 'utf-8')
230
- success('Created .opencode/.gitignore')
231
- }
232
-
233
- console.log()
234
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
235
- console.log(chalk.bold.green(' Join setup complete!'))
236
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
237
- console.log()
238
- console.log(' Your local environment is ready.')
239
- if (teamModels && Object.keys(teamModels).length > 0 && !hasUserOverride) {
240
- console.log(chalk.dim(' Tip: Run /make-user-model user <tier> current to override team models locally.'))
241
- }
242
- console.log(chalk.dim(' Restart opencode to pick up tier agent variants.'))
243
- console.log()
244
- }
1
+ import chalk from 'chalk'
2
+ import { execa } from 'execa'
3
+ import fse from 'fs-extra'
4
+ import path from 'node:path'
5
+ import { parse as parseJsonc } from 'jsonc-parser'
6
+ import { installBrowser } from '../steps/browser/index.js'
7
+ import { checkRtk } from '../steps/optimization/index.js'
8
+ import { checkPlatform } from '../steps/platform/index.js'
9
+ import { commandExists, header, info, loading, success, warn } from '../utils/exec.js'
10
+ import { IGNORED_ENTRIES, USER_CONFIG_FILE } from '../utils/paths.js'
11
+ import { readHarnessConfig } from './shared.js'
12
+
13
+ export async function runJoin() {
14
+ const logo = chalk.hex('#fe3d57')
15
+ console.log()
16
+ console.log(logo(' 🤝 agent-harness join'))
17
+ console.log(chalk.dim(' New team member setup: checks & local installs only.'))
18
+ console.log(chalk.dim(' Does not modify committed project files.'))
19
+ console.log()
20
+
21
+ const saved = await readHarnessConfig()
22
+
23
+ if (!saved) {
24
+ warn('No .opencode/harness.json found.')
25
+ warn('This project may not have been onboarded yet. Run `npx @plainconceptsplatform/agent-harness` first.')
26
+ return
27
+ }
28
+
29
+ const backlogPlatform = saved?.platform?.backlog
30
+ const repoPlatform = saved?.platform?.repo
31
+ const installScope = 'local'
32
+ const teamModels = saved?.models ?? {}
33
+
34
+ const opencodeDir = path.join(process.cwd(), '.opencode')
35
+ const opencodeJsonPath = path.join(process.cwd(), 'opencode.jsonc')
36
+
37
+ // Step 1: Platform CLI check
38
+ header('Step 1, Platform CLI check')
39
+ const platformsToCheck = [...new Set([backlogPlatform, repoPlatform])].filter(p => p && p !== 'none')
40
+ if (platformsToCheck.length === 0) {
41
+ info('No platform integration selected, skipping CLI checks.')
42
+ } else {
43
+ for (const p of platformsToCheck) {
44
+ const display = p === 'github' ? 'GitHub' : p === 'azure' ? 'Azure DevOps' : p
45
+ info(`Checking platform: ${display}`)
46
+ await checkPlatform(p)
47
+ }
48
+ }
49
+
50
+ // Step 2: Install OpenCode plugins
51
+ header('Step 2, Installing OpenCode plugins')
52
+ const pkgPath = path.join(opencodeDir, 'package.json')
53
+ if (await fse.pathExists(pkgPath)) {
54
+ try {
55
+ await execa('npm', ['install'], { cwd: opencodeDir, reject: false, stdio: 'pipe' })
56
+ success('OpenCode plugins installed')
57
+ } catch {
58
+ warn('npm install failed in .opencode/: plugins may not load correctly')
59
+ }
60
+ } else {
61
+ warn('No .opencode/package.json found: skipping plugin install')
62
+ }
63
+
64
+ // Step 3: Install/update skills
65
+ header('Step 3, Installing skills')
66
+ try {
67
+ await execa('npx', ['skills'], { cwd: process.cwd(), reject: false, stdio: 'pipe' })
68
+ success('Skills installed/updated')
69
+ } catch {
70
+ warn('npx skills failed: skills may be missing')
71
+ }
72
+
73
+ // Step 4: OpenSpec CLI check
74
+ header('Step 4, Checking OpenSpec')
75
+ const openspecAvailable = await commandExists('openspec')
76
+ if (openspecAvailable) {
77
+ success('OpenSpec CLI is available')
78
+ } else {
79
+ info('OpenSpec not found on PATH: installing...')
80
+ try {
81
+ const result = await execa('npm', ['install', '@fission-ai/openspec', '--global'], {
82
+ cwd: process.cwd(),
83
+ reject: false,
84
+ stdio: 'pipe',
85
+ })
86
+ if (result.exitCode === 0) {
87
+ success('OpenSpec installed')
88
+ } else {
89
+ warn('OpenSpec install failed: run `npm install -g @fission-ai/openspec` manually')
90
+ }
91
+ } catch {
92
+ warn('OpenSpec install failed: run `npm install -g @fission-ai/openspec` manually')
93
+ }
94
+ }
95
+
96
+ // Step 5: agentmemory (if project uses it)
97
+ header('Step 5, Checking agentmemory')
98
+ let cfg = {}
99
+ if (await fse.pathExists(opencodeJsonPath)) {
100
+ try {
101
+ const errors = []
102
+ cfg = parseJsonc(await fse.readFile(opencodeJsonPath, 'utf-8'), errors)
103
+ if (errors.length > 0) cfg = {}
104
+ } catch {
105
+ // ignore config read errors
106
+ }
107
+ }
108
+
109
+ if (cfg?.mcp?.['agentmemory']) {
110
+ info('Project uses agentmemory MCP')
111
+ loading('checking agentmemory server...')
112
+
113
+ // Check if agentmemory is installed
114
+ const agentmemoryAvailable = await commandExists('agentmemory')
115
+ if (agentmemoryAvailable) {
116
+ success('agentmemory CLI is available')
117
+ } else {
118
+ info('agentmemory not found on PATH, installing...')
119
+ try {
120
+ const installResult = await execa('npm', ['install', '-g', '@agentmemory/agentmemory'], {
121
+ reject: false,
122
+ timeout: 300000,
123
+ stdio: 'pipe',
124
+ })
125
+ if (installResult.exitCode === 0) {
126
+ success('agentmemory installed')
127
+ } else {
128
+ warn('agentmemory install failed, run `npm install -g @agentmemory/agentmemory` manually')
129
+ }
130
+ } catch (err) {
131
+ warn(`agentmemory install failed: ${err.message}`)
132
+ }
133
+ }
134
+
135
+ // Health-check the server
136
+ try {
137
+ const healthResult = await execa('curl', ['-s', '-o', '/dev/null', '-w', '%{http_code}', 'http://localhost:3111/agentmemory/health'], {
138
+ reject: false,
139
+ timeout: 5000,
140
+ })
141
+ if (healthResult.stdout.trim() === '200') {
142
+ success('agentmemory server is running')
143
+ } else {
144
+ info('agentmemory server not running, start it with `agentmemory` in a separate terminal')
145
+ }
146
+ } catch {
147
+ info('agentmemory server not running, start it with `agentmemory` in a separate terminal')
148
+ }
149
+ } else {
150
+ info('agentmemory not configured for this project, skipping')
151
+ }
152
+
153
+ // Step 6: Codegraph (if project uses it)
154
+ header('Step 6, Checking codegraph')
155
+ if (cfg?.mcp?.['codegraph']) {
156
+ info('Project uses codegraph MCP: building index...')
157
+ try {
158
+ const result = await execa('npx', ['--yes', '@colbymchenry/codegraph', 'init', '-i'], {
159
+ cwd: process.cwd(),
160
+ reject: false,
161
+ stdio: 'pipe',
162
+ timeout: 120000,
163
+ })
164
+ if (result.exitCode === 0) {
165
+ success('codegraph index initialized')
166
+ } else {
167
+ warn('codegraph init failed: codegraph_search may be slow or unavailable')
168
+ }
169
+ } catch {
170
+ warn('codegraph init failed: codegraph_search may be slow or unavailable')
171
+ }
172
+ } else {
173
+ info('codegraph not configured for this project: skipping')
174
+ }
175
+
176
+ // Step 7: RTK check
177
+ header('Step 7, Checking rtk')
178
+ await checkRtk({ skipHeader: true, skipPrompt: true })
179
+
180
+ // Step 8: Browser extension
181
+ header('Step 8, Installing opencode-browser')
182
+ await installBrowser({ installScope })
183
+
184
+ // Step 9: Model tier guidance
185
+ header('Step 9, Model tiers')
186
+ const userConfigPath = path.join(opencodeDir, USER_CONFIG_FILE)
187
+ const hasUserOverride = await fse.pathExists(userConfigPath)
188
+
189
+ if (teamModels && Object.keys(teamModels).length > 0) {
190
+ info('Team model configuration:')
191
+ for (const [tier, model] of Object.entries(teamModels)) {
192
+ info(` ${tier}: ${model}`)
193
+ }
194
+ console.log()
195
+
196
+ if (hasUserOverride) {
197
+ info('You have a local model override (harness.user.json).')
198
+ info('To change: /make-user-model user <tier> <model>')
199
+ } else {
200
+ info('No local override: using team defaults.')
201
+ info('To override a tier for your machine: /make-user-model user <tier> <model>')
202
+ info(' e.g. /make-user-model user build current')
203
+ }
204
+ console.log()
205
+ info('The pc-subagent-tiers plugin will generate *-engineer.<tier>.md variants')
206
+ info('on opencode startup from these model configs.')
207
+ } else {
208
+ warn('No model tiers configured in harness.json.')
209
+ warn('Run /make-user-model <tier> <model> for plan, build, and fast.')
210
+ }
211
+
212
+ // Step 10: Ensure .opencode/.gitignore exists
213
+ header('Step 10, Checking .opencode/.gitignore')
214
+ const gitignorePath = path.join(opencodeDir, '.gitignore')
215
+ const requiredEntries = IGNORED_ENTRIES
216
+
217
+ if (await fse.pathExists(gitignorePath)) {
218
+ const content = await fse.readFile(gitignorePath, 'utf-8')
219
+ const existing = content.split('\n').map(l => l.trim()).filter(Boolean)
220
+ const missing = requiredEntries.filter(e => !existing.includes(e))
221
+ if (missing.length > 0) {
222
+ const merged = `${[...existing, ...missing].join('\n')}\n`
223
+ await fse.writeFile(gitignorePath, merged, 'utf-8')
224
+ success(`Merged ${missing.length} missing .gitignore entries`)
225
+ } else {
226
+ success('.gitignore is up to date')
227
+ }
228
+ } else {
229
+ await fse.writeFile(gitignorePath, `${requiredEntries.join('\n')}\n`, 'utf-8')
230
+ success('Created .opencode/.gitignore')
231
+ }
232
+
233
+ console.log()
234
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
235
+ console.log(chalk.bold.green(' Join setup complete!'))
236
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
237
+ console.log()
238
+ console.log(' Your local environment is ready.')
239
+ if (teamModels && Object.keys(teamModels).length > 0 && !hasUserOverride) {
240
+ console.log(chalk.dim(' Tip: Run /make-user-model user <tier> current to override team models locally.'))
241
+ }
242
+ console.log(chalk.dim(' Restart opencode to pick up tier agent variants.'))
243
+ console.log()
244
+ }
@@ -1,27 +1,27 @@
1
- import { execa } from 'execa'
2
- import fse from 'fs-extra'
3
- import { configPath } from '../utils/paths.js'
4
-
5
- export async function readHarnessConfig() {
6
- const cfgPath = configPath()
7
- if (!await fse.pathExists(cfgPath)) return null
8
- try {
9
- return await fse.readJson(cfgPath)
10
- } catch {
11
- return null
12
- }
13
- }
14
-
15
- export async function ensureGitLongpaths(cwd = process.cwd()) {
16
- // core.longpaths only matters on Windows; don't touch repo config elsewhere.
17
- if (process.platform !== 'win32') return false
18
- try {
19
- const repoCheck = await execa('git', ['rev-parse', '--is-inside-work-tree'], { cwd, reject: false })
20
- if (repoCheck.exitCode !== 0 || repoCheck.stdout.trim() !== 'true') return false
21
-
22
- const configResult = await execa('git', ['config', 'core.longpaths', 'true'], { cwd, reject: false })
23
- return configResult.exitCode === 0
24
- } catch {
25
- return false
26
- }
27
- }
1
+ import { execa } from 'execa'
2
+ import fse from 'fs-extra'
3
+ import { configPath } from '../utils/paths.js'
4
+
5
+ export async function readHarnessConfig() {
6
+ const cfgPath = configPath()
7
+ if (!await fse.pathExists(cfgPath)) return null
8
+ try {
9
+ return await fse.readJson(cfgPath)
10
+ } catch {
11
+ return null
12
+ }
13
+ }
14
+
15
+ export async function ensureGitLongpaths(cwd = process.cwd()) {
16
+ // core.longpaths only matters on Windows; don't touch repo config elsewhere.
17
+ if (process.platform !== 'win32') return false
18
+ try {
19
+ const repoCheck = await execa('git', ['rev-parse', '--is-inside-work-tree'], { cwd, reject: false })
20
+ if (repoCheck.exitCode !== 0 || repoCheck.stdout.trim() !== 'true') return false
21
+
22
+ const configResult = await execa('git', ['config', 'core.longpaths', 'true'], { cwd, reject: false })
23
+ return configResult.exitCode === 0
24
+ } catch {
25
+ return false
26
+ }
27
+ }