@plainconceptsplatform/agent-harness 2.3.0 → 2.4.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 (155) hide show
  1. package/README.md +23 -12
  2. package/{src → cli}/commands/join.js +3 -4
  3. package/{src → cli}/commands/migrate.js +1 -1
  4. package/{src → cli}/commands/wizard.js +1 -1
  5. package/{src → cli}/index.js +1 -1
  6. package/{src → cli}/presets/agents-content.json +4 -4
  7. package/cli/presets/browser.json +10 -0
  8. package/{src → cli}/presets/clean.json +21 -21
  9. package/{src → cli}/presets/optimization.json +37 -37
  10. package/{src → cli}/presets/platforms.json +76 -76
  11. package/{src → cli}/presets/quota.json +16 -16
  12. package/{src → cli}/presets/source.json +23 -23
  13. package/cli/steps/browser/index.js +48 -0
  14. package/{src → cli}/steps/clean/index.js +120 -120
  15. package/{src → cli}/steps/copy/agents.js +119 -119
  16. package/{src → cli}/steps/copy/commands.js +95 -95
  17. package/{src → cli}/steps/copy/fullstack-engineer.js +89 -88
  18. package/{src → cli}/steps/copy/index.js +1 -1
  19. package/{src → cli}/steps/copy/opencode-json.js +196 -161
  20. package/{src → cli}/steps/copy/skills.js +2 -2
  21. package/{src → cli}/steps/models/format.js +88 -88
  22. package/{src → cli}/steps/models/index.js +64 -64
  23. package/{src → cli}/steps/openspec/index.js +136 -136
  24. package/{src → cli}/steps/optimization/codegraph.js +127 -127
  25. package/{src → cli}/steps/optimization/detect.js +66 -66
  26. package/{src → cli}/steps/optimization/humanizer.js +17 -17
  27. package/{src → cli}/steps/optimization/index.js +163 -163
  28. package/{src → cli}/steps/optimization/memory.js +88 -88
  29. package/{src → cli}/steps/optimization/patch-guardrails.js +109 -109
  30. package/{src → cli}/steps/optimization/quota.js +119 -119
  31. package/{src → cli}/steps/optimization/simple-english.js +17 -17
  32. package/{src → cli}/steps/optimization/skills-lock.js +30 -30
  33. package/{src → cli}/steps/platform/index.js +109 -109
  34. package/{src → cli}/steps/source/index.js +123 -123
  35. package/{src → cli}/utils/agent-color.js +111 -83
  36. package/{src → cli}/utils/exec-spinner.js +47 -47
  37. package/{src → cli}/utils/exec.js +134 -134
  38. package/{src → cli}/utils/models-pricing.js +42 -42
  39. package/{src → cli}/utils/paths.js +1 -1
  40. package/{src → cli}/utils/process.js +3 -3
  41. package/{src → cli}/utils/terminal.js +6 -6
  42. package/harness/.agents/skills/browser-automation/SKILL.md +72 -0
  43. package/{src/content → harness}/.agents/skills/pc-make-engineer/SKILL.md +219 -219
  44. package/{src/content → harness}/.agents/skills/pc-make-engineer/template.md +80 -80
  45. package/{src/content → harness}/.agents/skills/pc-plan-apply/simple-mode.md +1 -1
  46. package/{src/content → harness}/.agents/skills/pc-plan-archive/SKILL.md +3 -0
  47. package/{src/content → harness}/.agents/skills/pc-plan-explore/SKILL.md +3 -0
  48. package/{src/content → harness}/.agents/skills/pc-plan-goal/SKILL.md +11 -20
  49. package/{src/content → harness}/.agents/skills/pc-plan-goal/branching.md +1 -1
  50. package/{src/content → harness}/.agents/skills/pc-plan-goal/output.md +5 -8
  51. package/{src/content → harness}/.agents/skills/pc-plan-story/SKILL.md +3 -0
  52. package/{src/content → harness}/.agents/skills/pc-userstory-browser/SKILL.md +136 -132
  53. package/{src/content → harness}/.opencode/package.json +9 -10
  54. package/{src/content → harness}/.opencode/plugins/pc-subagent-tiers.js +368 -337
  55. package/{src/content → harness}/ARCHITECTURE.md +16 -16
  56. package/{src/content → harness}/DESIGN.md +16 -16
  57. package/{src/content → harness}/opencode.jsonc +47 -41
  58. package/{src/content → harness}/openspec/config.yaml +20 -20
  59. package/{src/content → harness}/skills-lock.json +17 -17
  60. package/package.json +7 -7
  61. package/src/content/.agents/skills/browser-automation/SKILL.md +0 -66
  62. package/src/presets/browser.json +0 -22
  63. package/src/steps/browser/index.js +0 -91
  64. /package/{src → cli}/commands/shared.js +0 -0
  65. /package/{src → cli}/commands/single.js +0 -0
  66. /package/{src → cli}/commands/update.js +0 -0
  67. /package/{src → cli}/fragments/archive/az.md +0 -0
  68. /package/{src → cli}/fragments/archive/gh.md +0 -0
  69. /package/{src → cli}/fragments/archive/gl.md +0 -0
  70. /package/{src → cli}/fragments/archive/none.md +0 -0
  71. /package/{src → cli}/fragments/guardrails/codegraph.md +0 -0
  72. /package/{src → cli}/fragments/guardrails/humanizer.md +0 -0
  73. /package/{src → cli}/fragments/guardrails/memory.md +0 -0
  74. /package/{src → cli}/fragments/guardrails/rtk.md +0 -0
  75. /package/{src → cli}/fragments/guardrails/simple-english.md +0 -0
  76. /package/{src → cli}/fragments/ops-backlog/az.md +0 -0
  77. /package/{src → cli}/fragments/ops-backlog/gh.md +0 -0
  78. /package/{src → cli}/fragments/ops-backlog/jira.md +0 -0
  79. /package/{src → cli}/fragments/ops-evidence/az.md +0 -0
  80. /package/{src → cli}/fragments/ops-evidence/gh.md +0 -0
  81. /package/{src → cli}/fragments/ops-evidence/jira.md +0 -0
  82. /package/{src → cli}/fragments/ops-review/az.md +0 -0
  83. /package/{src → cli}/fragments/ops-review/gh.md +0 -0
  84. /package/{src → cli}/fragments/ops-review/gl.md +0 -0
  85. /package/{src → cli}/fragments/ops-ship/az.md +0 -0
  86. /package/{src → cli}/fragments/ops-ship/gh.md +0 -0
  87. /package/{src → cli}/fragments/ops-ship/gl.md +0 -0
  88. /package/{src → cli}/presets/models.json +0 -0
  89. /package/{src → cli}/presets/openspec.json +0 -0
  90. /package/{src → cli}/steps/metadata/index.js +0 -0
  91. /package/{src → cli}/steps/models/write.js +0 -0
  92. /package/{src → cli}/utils/copy.js +0 -0
  93. /package/{src → cli}/utils/legacy-check.js +0 -0
  94. /package/{src → cli}/utils/models-cache.js +0 -0
  95. /package/{src → cli}/utils/update-manifest.js +0 -0
  96. /package/{src/content → harness}/.agents/skills/pc-guardrails-generic/SKILL.md +0 -0
  97. /package/{src/content → harness}/.agents/skills/pc-guardrails-project/SKILL.md +0 -0
  98. /package/{src/content → harness}/.agents/skills/pc-make-architecture/SKILL.md +0 -0
  99. /package/{src/content → harness}/.agents/skills/pc-make-architecture/structure-template.md +0 -0
  100. /package/{src/content → harness}/.agents/skills/pc-make-design/SKILL.md +0 -0
  101. /package/{src/content → harness}/.agents/skills/pc-make-engineer/signal-mapping.md +0 -0
  102. /package/{src/content → harness}/.agents/skills/pc-make-evidence-scaffold/SKILL.md +0 -0
  103. /package/{src/content → harness}/.agents/skills/pc-make-evidence-scaffold/evidence-contract.md +0 -0
  104. /package/{src/content → harness}/.agents/skills/pc-make-guardrails/SKILL.md +0 -0
  105. /package/{src/content → harness}/.agents/skills/pc-make-guardrails/category-reference.md +0 -0
  106. /package/{src/content → harness}/.agents/skills/pc-make-merge-risk-assess/SKILL.md +0 -0
  107. /package/{src/content → harness}/.agents/skills/pc-make-merge-risk-assess/category-reference.md +0 -0
  108. /package/{src/content → harness}/.agents/skills/pc-make-user-model/SKILL.md +0 -0
  109. /package/{src/content → harness}/.agents/skills/pc-ops-evidence/SKILL.md +0 -0
  110. /package/{src/content → harness}/.agents/skills/pc-ops-ship/SKILL.md +0 -0
  111. /package/{src/content → harness}/.agents/skills/pc-plan-apply/SKILL.md +0 -0
  112. /package/{src/content → harness}/.agents/skills/pc-plan-goal/failure-policy.md +0 -0
  113. /package/{src/content → harness}/.agents/skills/pc-plan-goal/output-mode.md +0 -0
  114. /package/{src/content → harness}/.agents/skills/pc-plan-propose/SKILL.md +0 -0
  115. /package/{src/content → harness}/.agents/skills/pc-plan-propose/task-annotation.md +0 -0
  116. /package/{src/content → harness}/.agents/skills/pc-plan-quick/SKILL.md +0 -0
  117. /package/{src/content → harness}/.agents/skills/pc-repo-audit/SKILL.md +0 -0
  118. /package/{src/content → harness}/.agents/skills/pc-repo-help/SKILL.md +0 -0
  119. /package/{src/content → harness}/.agents/skills/pc-repo-initialize/SKILL.md +0 -0
  120. /package/{src/content → harness}/.agents/skills/pc-repo-onboard/SKILL.md +0 -0
  121. /package/{src/content → harness}/.agents/skills/pc-repo-verify/SKILL.md +0 -0
  122. /package/{src/content → harness}/.agents/skills/pc-userstory-az/SKILL.md +0 -0
  123. /package/{src/content → harness}/.agents/skills/pc-userstory-gh/SKILL.md +0 -0
  124. /package/{src/content → harness}/.agents/skills/pc-userstory-jira/SKILL.md +0 -0
  125. /package/{src/content → harness}/.opencode/_gitignore +0 -0
  126. /package/{src/content → harness}/.opencode/commands/init.md +0 -0
  127. /package/{src/content → harness}/.opencode/commands/make-architecture.md +0 -0
  128. /package/{src/content → harness}/.opencode/commands/make-design.md +0 -0
  129. /package/{src/content → harness}/.opencode/commands/make-engineer.md +0 -0
  130. /package/{src/content → harness}/.opencode/commands/make-evidence-scaffold.md +0 -0
  131. /package/{src/content → harness}/.opencode/commands/make-guardrails.md +0 -0
  132. /package/{src/content → harness}/.opencode/commands/make-user-model.md +0 -0
  133. /package/{src/content → harness}/.opencode/commands/ops-backlog.md +0 -0
  134. /package/{src/content → harness}/.opencode/commands/ops-evidence.md +0 -0
  135. /package/{src/content → harness}/.opencode/commands/ops-review.md +0 -0
  136. /package/{src/content → harness}/.opencode/commands/ops-ship.md +0 -0
  137. /package/{src/content → harness}/.opencode/commands/plan-apply.md +0 -0
  138. /package/{src/content → harness}/.opencode/commands/plan-archive.md +0 -0
  139. /package/{src/content → harness}/.opencode/commands/plan-explore.md +0 -0
  140. /package/{src/content → harness}/.opencode/commands/plan-goal.md +0 -0
  141. /package/{src/content → harness}/.opencode/commands/plan-propose.md +0 -0
  142. /package/{src/content → harness}/.opencode/commands/plan-quick.md +0 -0
  143. /package/{src/content → harness}/.opencode/commands/plan-story.md +0 -0
  144. /package/{src/content → harness}/.opencode/commands/repo-audit.md +0 -0
  145. /package/{src/content → harness}/.opencode/commands/repo-help.md +0 -0
  146. /package/{src/content → harness}/.opencode/commands/repo-initialize.md +0 -0
  147. /package/{src/content → harness}/.opencode/commands/repo-onboard.md +0 -0
  148. /package/{src/content → harness}/.opencode/commands/repo-verify.md +0 -0
  149. /package/{src/content → harness}/.opencode/plugins/pc-subagent-monitor.js +0 -0
  150. /package/{src/content → harness}/.opencode/plugins/pc-system-reminders.js +0 -0
  151. /package/{src/content → harness}/.opencode/tui/pc-subagents.tsx +0 -0
  152. /package/{src/content → harness}/.opencode/tui.json +0 -0
  153. /package/{src/content → harness}/AGENTS.md +0 -0
  154. /package/{src/content → harness}/openspec/changes/archive/.gitkeep +0 -0
  155. /package/{src/content → harness}/openspec/specs/.gitkeep +0 -0
@@ -1,95 +1,95 @@
1
- import fse from 'fs-extra'
2
- import path from 'path'
3
- import { fileURLToPath } from 'url'
4
- import { success } from '../../utils/exec.js'
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
-
8
- const _archive = {
9
- azure: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/az.md'), 'utf-8'),
10
- github: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/gh.md'), 'utf-8'),
11
- gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/gl.md'), 'utf-8'),
12
- none: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/none.md'), 'utf-8'),
13
- }
14
-
15
- const _ship = {
16
- github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/gh.md'), 'utf-8'),
17
- azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/az.md'), 'utf-8'),
18
- gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/gl.md'), 'utf-8'),
19
- }
20
-
21
- const _review = {
22
- github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/gh.md'), 'utf-8'),
23
- azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/az.md'), 'utf-8'),
24
- gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/gl.md'), 'utf-8'),
25
- }
26
-
27
- const _backlog = {
28
- github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/gh.md'), 'utf-8'),
29
- azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/az.md'), 'utf-8'),
30
- jira: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/jira.md'), 'utf-8'),
31
- }
32
-
33
- // Evidence comments target the backlog platform (where the issue/work-item lives).
34
- const _evidence = {
35
- github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/gh.md'), 'utf-8'),
36
- azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/az.md'), 'utf-8'),
37
- jira: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/jira.md'), 'utf-8'),
38
- }
39
-
40
- // relativePath is POSIX-style relative to the project root; commands live in
41
- // .opencode/commands/, skill-backed procedures (plan-archive, ops-ship) in
42
- // .agents/skills/<name>/SKILL.md.
43
- function patchFile(relativePath, startMarker, endMarker, content, platform, cwd = process.cwd()) {
44
- const targetPath = path.join(cwd, ...relativePath.split('/'))
45
- if (!fse.pathExistsSync(targetPath)) return
46
-
47
- let fileContent = fse.readFileSync(targetPath, 'utf-8')
48
- if (!fileContent.includes(startMarker) || !fileContent.includes(endMarker)) return
49
-
50
- const pattern = new RegExp(`${startMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${endMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)
51
- // Replacer function, not a replacement string: the injected fragments contain
52
- // shell quoting like $'...' and a string replacement expands $' as "everything
53
- // after the match", silently truncating the command. That is how the ops-ship
54
- // screenshot comment lost its entire --body argument.
55
- fileContent = fileContent.replace(pattern, () => `${startMarker}\n${content.trim()}\n${endMarker}`)
56
- fse.writeFileSync(targetPath, `${fileContent.replace(/\s*$/, '')}\n`, 'utf-8')
57
- success(`${relativePath} content injected for platform: ${platform}`)
58
- }
59
-
60
- export async function patchArchiveCommand(platform, cwd = process.cwd()) {
61
- const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? platform.backlogPlatform ?? 'github') : platform
62
- const replacement = _archive[repoPlatform]
63
- if (!replacement) return
64
- patchFile('.agents/skills/pc-plan-archive/SKILL.md', '<!-- PC-PLATFORM-ARCHIVE-START -->', '<!-- PC-PLATFORM-ARCHIVE-END -->', replacement, repoPlatform, cwd)
65
- }
66
-
67
- export async function patchOpsShip(platform, cwd = process.cwd()) {
68
- const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? 'github') : platform
69
- const replacement = _ship[repoPlatform]
70
- if (!replacement) return
71
- patchFile('.agents/skills/pc-ops-ship/SKILL.md', '<!-- PC-PLATFORM-SHIP-START -->', '<!-- PC-PLATFORM-SHIP-END -->', replacement, repoPlatform, cwd)
72
- }
73
-
74
- export async function patchOpsReview(platform, cwd = process.cwd()) {
75
- const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? 'github') : platform
76
- const replacement = _review[repoPlatform]
77
- if (!replacement) return
78
- patchFile('.opencode/commands/ops-review.md', '<!-- PC-PLATFORM-REVIEW-START -->', '<!-- PC-PLATFORM-REVIEW-END -->', replacement, repoPlatform, cwd)
79
- }
80
-
81
- export async function patchOpsBacklog(platform, cwd = process.cwd()) {
82
- const backlogPlatform = typeof platform === 'object' ? (platform.backlogPlatform ?? 'github') : platform
83
- const replacement = _backlog[backlogPlatform]
84
- if (!replacement) return
85
- patchFile('.opencode/commands/ops-backlog.md', '<!-- PC-PLATFORM-BACKLOG-START -->', '<!-- PC-PLATFORM-BACKLOG-END -->', replacement, backlogPlatform, cwd)
86
- }
87
-
88
- export async function patchOpsEvidence(platform, cwd = process.cwd()) {
89
- const backlogPlatform = typeof platform === 'object' ? (platform.backlogPlatform ?? 'github') : platform
90
- const replacement = _evidence[backlogPlatform]
91
- // browser/none have no CLI to post with: leave the markers empty (the skill skips commenting).
92
- if (!replacement) return
93
- patchFile('.agents/skills/pc-ops-evidence/SKILL.md', '<!-- PC-PLATFORM-EVIDENCE-START -->', '<!-- PC-PLATFORM-EVIDENCE-END -->', replacement, backlogPlatform, cwd)
94
- }
95
-
1
+ import fse from 'fs-extra'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+ import { success } from '../../utils/exec.js'
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
+
8
+ const _archive = {
9
+ azure: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/az.md'), 'utf-8'),
10
+ github: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/gh.md'), 'utf-8'),
11
+ gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/gl.md'), 'utf-8'),
12
+ none: await fse.readFile(path.resolve(__dirname, '../../fragments/archive/none.md'), 'utf-8'),
13
+ }
14
+
15
+ const _ship = {
16
+ github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/gh.md'), 'utf-8'),
17
+ azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/az.md'), 'utf-8'),
18
+ gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-ship/gl.md'), 'utf-8'),
19
+ }
20
+
21
+ const _review = {
22
+ github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/gh.md'), 'utf-8'),
23
+ azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/az.md'), 'utf-8'),
24
+ gitlab: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-review/gl.md'), 'utf-8'),
25
+ }
26
+
27
+ const _backlog = {
28
+ github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/gh.md'), 'utf-8'),
29
+ azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/az.md'), 'utf-8'),
30
+ jira: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-backlog/jira.md'), 'utf-8'),
31
+ }
32
+
33
+ // Evidence comments target the backlog platform (where the issue/work-item lives).
34
+ const _evidence = {
35
+ github: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/gh.md'), 'utf-8'),
36
+ azure: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/az.md'), 'utf-8'),
37
+ jira: await fse.readFile(path.resolve(__dirname, '../../fragments/ops-evidence/jira.md'), 'utf-8'),
38
+ }
39
+
40
+ // relativePath is POSIX-style relative to the project root; commands live in
41
+ // .opencode/commands/, skill-backed procedures (plan-archive, ops-ship) in
42
+ // .agents/skills/<name>/SKILL.md.
43
+ function patchFile(relativePath, startMarker, endMarker, content, platform, cwd = process.cwd()) {
44
+ const targetPath = path.join(cwd, ...relativePath.split('/'))
45
+ if (!fse.pathExistsSync(targetPath)) return
46
+
47
+ let fileContent = fse.readFileSync(targetPath, 'utf-8')
48
+ if (!fileContent.includes(startMarker) || !fileContent.includes(endMarker)) return
49
+
50
+ const pattern = new RegExp(`${startMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${endMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)
51
+ // Replacer function, not a replacement string: the injected fragments contain
52
+ // shell quoting like $'...' and a string replacement expands $' as "everything
53
+ // after the match", silently truncating the command. That is how the ops-ship
54
+ // screenshot comment lost its entire --body argument.
55
+ fileContent = fileContent.replace(pattern, () => `${startMarker}\n${content.trim()}\n${endMarker}`)
56
+ fse.writeFileSync(targetPath, `${fileContent.replace(/\s*$/, '')}\n`, 'utf-8')
57
+ success(`${relativePath} content injected for platform: ${platform}`)
58
+ }
59
+
60
+ export async function patchArchiveCommand(platform, cwd = process.cwd()) {
61
+ const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? platform.backlogPlatform ?? 'github') : platform
62
+ const replacement = _archive[repoPlatform]
63
+ if (!replacement) return
64
+ patchFile('.agents/skills/pc-plan-archive/SKILL.md', '<!-- PC-PLATFORM-ARCHIVE-START -->', '<!-- PC-PLATFORM-ARCHIVE-END -->', replacement, repoPlatform, cwd)
65
+ }
66
+
67
+ export async function patchOpsShip(platform, cwd = process.cwd()) {
68
+ const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? 'github') : platform
69
+ const replacement = _ship[repoPlatform]
70
+ if (!replacement) return
71
+ patchFile('.agents/skills/pc-ops-ship/SKILL.md', '<!-- PC-PLATFORM-SHIP-START -->', '<!-- PC-PLATFORM-SHIP-END -->', replacement, repoPlatform, cwd)
72
+ }
73
+
74
+ export async function patchOpsReview(platform, cwd = process.cwd()) {
75
+ const repoPlatform = typeof platform === 'object' ? (platform.repoPlatform ?? 'github') : platform
76
+ const replacement = _review[repoPlatform]
77
+ if (!replacement) return
78
+ patchFile('.opencode/commands/ops-review.md', '<!-- PC-PLATFORM-REVIEW-START -->', '<!-- PC-PLATFORM-REVIEW-END -->', replacement, repoPlatform, cwd)
79
+ }
80
+
81
+ export async function patchOpsBacklog(platform, cwd = process.cwd()) {
82
+ const backlogPlatform = typeof platform === 'object' ? (platform.backlogPlatform ?? 'github') : platform
83
+ const replacement = _backlog[backlogPlatform]
84
+ if (!replacement) return
85
+ patchFile('.opencode/commands/ops-backlog.md', '<!-- PC-PLATFORM-BACKLOG-START -->', '<!-- PC-PLATFORM-BACKLOG-END -->', replacement, backlogPlatform, cwd)
86
+ }
87
+
88
+ export async function patchOpsEvidence(platform, cwd = process.cwd()) {
89
+ const backlogPlatform = typeof platform === 'object' ? (platform.backlogPlatform ?? 'github') : platform
90
+ const replacement = _evidence[backlogPlatform]
91
+ // browser/none have no CLI to post with: leave the markers empty (the skill skips commenting).
92
+ if (!replacement) return
93
+ patchFile('.agents/skills/pc-ops-evidence/SKILL.md', '<!-- PC-PLATFORM-EVIDENCE-START -->', '<!-- PC-PLATFORM-EVIDENCE-END -->', replacement, backlogPlatform, cwd)
94
+ }
95
+
@@ -1,88 +1,89 @@
1
- import fse from 'fs-extra'
2
- import path from 'path'
3
- import { success } from '../../utils/exec.js'
4
- import { agentColor } from '../../utils/agent-color.js'
5
-
6
- const FULLSTACK_FILE = 'fullstack-engineer.md'
7
- const FULLSTACK_DESCRIPTION = 'Default engineer that accumulates skills from all created persona engineers. Use as fallback when no specialist matches: but prefer spawning a specific engineer for deterministic results.'
8
- const FULLSTACK_IDENTITY = 'You are the default engineer, and your body is what the build and plan agents run. You are more complete but less accurate than specialized engineers, so prefer spawning a specialist when one matches the task domain.'
9
-
10
- // The reminder plugin owns ability bootstrap. Remove the old prompt-level
11
- // directive when regenerating so it cannot duplicate the plugin reminder.
12
- function stripStartupDirective(text) {
13
- if (!text) return text
14
- return text
15
- .split(/\r?\n\r?\n+/)
16
- .filter(paragraph => !paragraph.trim().startsWith('**Startup'))
17
- .join('\n\n')
18
- .trim()
19
- }
20
-
21
- export async function removeLegacyStartupDirectives({ cwd = process.cwd() } = {}) {
22
- const agentsDir = path.join(cwd, '.opencode', 'agents')
23
- if (!await fse.pathExists(agentsDir)) return 0
24
-
25
- let updated = 0
26
- for (const fileName of await fse.readdir(agentsDir)) {
27
- if (!fileName.endsWith('.md')) continue
28
- const filePath = path.join(agentsDir, fileName)
29
- const content = await fse.readFile(filePath, 'utf-8')
30
- const cleaned = stripStartupDirective(content)
31
- if (cleaned === content.trim()) continue
32
- await fse.writeFile(filePath, `${cleaned}\n`, 'utf-8')
33
- updated++
34
- }
35
- return updated
36
- }
37
-
38
- export async function generateFullstackEngineer({ cwd = process.cwd(), updateMode = false } = {}) {
39
- const agentsDir = path.join(cwd, '.opencode', 'agents')
40
- await fse.ensureDir(agentsDir)
41
- const filePath = path.join(agentsDir, FULLSTACK_FILE)
42
-
43
- if (updateMode && await fse.pathExists(filePath)) {
44
- return { generated: false, preserved: true }
45
- }
46
-
47
- let existingAbilities = null
48
- let existingIdentity = null
49
- if (await fse.pathExists(filePath)) {
50
- const content = await fse.readFile(filePath, 'utf-8')
51
- const abilitiesMatch = content.match(/## Abilities\n([\s\S]*?)$/)
52
- if (abilitiesMatch) existingAbilities = abilitiesMatch[1].trim()
53
- const identityMatch = content.match(/^---\n[\s\S]*?\n---\n\n(.*?)(?:\n\n## Abilities)/s)
54
- if (identityMatch) existingIdentity = stripStartupDirective(identityMatch[1])
55
- }
56
-
57
- const frontmatter = [
58
- '---',
59
- `description: ${FULLSTACK_DESCRIPTION}`,
60
- // subagent, not primary: build.md and plan.md are the primaries, and
61
- // pc-subagent-tiers generates both from this file on every startup.
62
- 'mode: subagent',
63
- // warning is reserved for plan, so fullstack takes its name-derived hex
64
- // like every other subagent.
65
- `color: ${agentColor('fullstack-engineer')}`,
66
- 'permission:',
67
- ' edit: allow',
68
- ' bash: allow',
69
- ' read: allow',
70
- ' glob: allow',
71
- ' grep: allow',
72
- ' question: allow',
73
- ' todowrite: allow',
74
- '---',
75
- ]
76
-
77
- const identity = existingIdentity ?? FULLSTACK_IDENTITY
78
-
79
- const abilities = existingAbilities ?? [
80
- '- Guardrails: @pc-guardrails-generic, @pc-guardrails-project',
81
- ].join('\n')
82
-
83
- const content = `${frontmatter.join('\n')}\n\n${identity}\n\n## Abilities\n${abilities}\n`
84
-
85
- await fse.writeFile(filePath, content, 'utf-8')
86
- success(`Generated ${FULLSTACK_FILE}`)
87
- return { generated: true }
88
- }
1
+ import fse from 'fs-extra'
2
+ import path from 'path'
3
+ import { success } from '../../utils/exec.js'
4
+ import { agentColor, yamlColor } from '../../utils/agent-color.js'
5
+
6
+ const FULLSTACK_FILE = 'fullstack-engineer.md'
7
+ const FULLSTACK_DESCRIPTION = 'Default engineer that accumulates skills from all created persona engineers. Use as fallback when no specialist matches: but prefer spawning a specific engineer for deterministic results.'
8
+ const FULLSTACK_IDENTITY = 'You are the default engineer, and your body is what the build and plan agents run. You are more complete but less accurate than specialized engineers, so prefer spawning a specialist when one matches the task domain.'
9
+
10
+ // The reminder plugin owns ability bootstrap. Remove the old prompt-level
11
+ // directive when regenerating so it cannot duplicate the plugin reminder.
12
+ function stripStartupDirective(text) {
13
+ if (!text) return text
14
+ return text
15
+ .split(/\r?\n\r?\n+/)
16
+ .filter(paragraph => !paragraph.trim().startsWith('**Startup'))
17
+ .join('\n\n')
18
+ .trim()
19
+ }
20
+
21
+ export async function removeLegacyStartupDirectives({ cwd = process.cwd() } = {}) {
22
+ const agentsDir = path.join(cwd, '.opencode', 'agents')
23
+ if (!await fse.pathExists(agentsDir)) return 0
24
+
25
+ let updated = 0
26
+ for (const fileName of await fse.readdir(agentsDir)) {
27
+ if (!fileName.endsWith('.md')) continue
28
+ const filePath = path.join(agentsDir, fileName)
29
+ const content = await fse.readFile(filePath, 'utf-8')
30
+ const cleaned = stripStartupDirective(content)
31
+ if (cleaned === content.trim()) continue
32
+ await fse.writeFile(filePath, `${cleaned}\n`, 'utf-8')
33
+ updated++
34
+ }
35
+ return updated
36
+ }
37
+
38
+ export async function generateFullstackEngineer({ cwd = process.cwd(), updateMode = false } = {}) {
39
+ const agentsDir = path.join(cwd, '.opencode', 'agents')
40
+ await fse.ensureDir(agentsDir)
41
+ const filePath = path.join(agentsDir, FULLSTACK_FILE)
42
+
43
+ if (updateMode && await fse.pathExists(filePath)) {
44
+ return { generated: false, preserved: true }
45
+ }
46
+
47
+ let existingAbilities = null
48
+ let existingIdentity = null
49
+ if (await fse.pathExists(filePath)) {
50
+ const content = await fse.readFile(filePath, 'utf-8')
51
+ const abilitiesMatch = content.match(/## Abilities\n([\s\S]*?)$/)
52
+ if (abilitiesMatch) existingAbilities = abilitiesMatch[1].trim()
53
+ const identityMatch = content.match(/^---\n[\s\S]*?\n---\n\n(.*?)(?:\n\n## Abilities)/s)
54
+ if (identityMatch) existingIdentity = stripStartupDirective(identityMatch[1])
55
+ }
56
+
57
+ const frontmatter = [
58
+ '---',
59
+ `description: ${FULLSTACK_DESCRIPTION}`,
60
+ // subagent, not primary: build.md and plan.md are the primaries, and
61
+ // pc-subagent-tiers generates both from this file on every startup.
62
+ 'mode: subagent',
63
+ // warning is reserved for plan, so fullstack takes its name-derived hex
64
+ // like every other subagent.
65
+ // Quoted: a bare #hex is a YAML comment, which opencode rejects.
66
+ `color: ${yamlColor(agentColor('fullstack-engineer'))}`,
67
+ 'permission:',
68
+ ' edit: allow',
69
+ ' bash: allow',
70
+ ' read: allow',
71
+ ' glob: allow',
72
+ ' grep: allow',
73
+ ' question: allow',
74
+ ' todowrite: allow',
75
+ '---',
76
+ ]
77
+
78
+ const identity = existingIdentity ?? FULLSTACK_IDENTITY
79
+
80
+ const abilities = existingAbilities ?? [
81
+ '- Guardrails: @pc-guardrails-generic, @pc-guardrails-project',
82
+ ].join('\n')
83
+
84
+ const content = `${frontmatter.join('\n')}\n\n${identity}\n\n## Abilities\n${abilities}\n`
85
+
86
+ await fse.writeFile(filePath, content, 'utf-8')
87
+ success(`Generated ${FULLSTACK_FILE}`)
88
+ return { generated: true }
89
+ }
@@ -11,7 +11,7 @@ import { generateFullstackEngineer, removeLegacyStartupDirectives } from "./full
11
11
  import { patchOpencodeJson, patchOpencodePackage } from "./opencode-json.js"
12
12
 
13
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
14
- const CONTENT_DIR = path.resolve(__dirname, "../../content")
14
+ const CONTENT_DIR = path.resolve(__dirname, "../../../harness")
15
15
 
16
16
  export async function copyContentStep(platform, ctx = {}) {
17
17
  header("Step 5, Copying agent-harness files")