@plainconceptsplatform/agent-harness 2.3.0 → 2.4.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/README.md +23 -12
- package/{src → cli}/commands/join.js +3 -4
- package/{src → cli}/commands/migrate.js +1 -1
- package/{src → cli}/commands/wizard.js +1 -1
- package/{src → cli}/index.js +1 -1
- package/{src → cli}/presets/agents-content.json +4 -4
- package/cli/presets/browser.json +10 -0
- package/{src → cli}/presets/clean.json +21 -21
- package/{src → cli}/presets/optimization.json +37 -37
- package/{src → cli}/presets/platforms.json +76 -76
- package/{src → cli}/presets/quota.json +16 -16
- package/{src → cli}/presets/source.json +23 -23
- package/cli/steps/browser/index.js +48 -0
- package/{src → cli}/steps/clean/index.js +120 -120
- package/{src → cli}/steps/copy/agents.js +119 -119
- package/{src → cli}/steps/copy/commands.js +95 -95
- package/{src → cli}/steps/copy/fullstack-engineer.js +89 -88
- package/{src → cli}/steps/copy/index.js +1 -1
- package/{src → cli}/steps/copy/opencode-json.js +196 -161
- package/{src → cli}/steps/copy/skills.js +2 -2
- package/{src → cli}/steps/models/format.js +88 -88
- package/{src → cli}/steps/models/index.js +64 -64
- package/{src → cli}/steps/openspec/index.js +136 -136
- package/{src → cli}/steps/optimization/codegraph.js +127 -127
- package/{src → cli}/steps/optimization/detect.js +66 -66
- package/{src → cli}/steps/optimization/humanizer.js +17 -17
- package/{src → cli}/steps/optimization/index.js +163 -163
- package/{src → cli}/steps/optimization/memory.js +88 -88
- package/{src → cli}/steps/optimization/patch-guardrails.js +109 -109
- package/{src → cli}/steps/optimization/quota.js +119 -119
- package/{src → cli}/steps/optimization/simple-english.js +17 -17
- package/{src → cli}/steps/optimization/skills-lock.js +30 -30
- package/{src → cli}/steps/platform/index.js +109 -109
- package/{src → cli}/steps/source/index.js +123 -123
- package/{src → cli}/utils/agent-color.js +111 -83
- package/{src → cli}/utils/exec-spinner.js +47 -47
- package/{src → cli}/utils/exec.js +134 -134
- package/{src → cli}/utils/models-pricing.js +42 -42
- package/{src → cli}/utils/paths.js +1 -1
- package/{src → cli}/utils/process.js +3 -3
- package/{src → cli}/utils/terminal.js +6 -6
- package/harness/.agents/skills/browser-automation/SKILL.md +72 -0
- package/{src/content → harness}/.agents/skills/pc-make-engineer/SKILL.md +219 -219
- package/{src/content → harness}/.agents/skills/pc-make-engineer/template.md +80 -80
- package/{src/content → harness}/.agents/skills/pc-plan-archive/SKILL.md +3 -0
- package/{src/content → harness}/.agents/skills/pc-plan-explore/SKILL.md +3 -0
- package/{src/content → harness}/.agents/skills/pc-plan-goal/SKILL.md +5 -16
- package/{src/content → harness}/.agents/skills/pc-plan-goal/branching.md +1 -1
- package/{src/content → harness}/.agents/skills/pc-plan-goal/output.md +5 -8
- package/{src/content → harness}/.agents/skills/pc-plan-story/SKILL.md +3 -0
- package/{src/content → harness}/.agents/skills/pc-userstory-browser/SKILL.md +136 -132
- package/{src/content → harness}/.opencode/package.json +9 -10
- package/{src/content → harness}/.opencode/plugins/pc-subagent-tiers.js +368 -337
- package/{src/content → harness}/ARCHITECTURE.md +16 -16
- package/{src/content → harness}/DESIGN.md +16 -16
- package/{src/content → harness}/opencode.jsonc +47 -41
- package/{src/content → harness}/openspec/config.yaml +20 -20
- package/{src/content → harness}/skills-lock.json +17 -17
- package/package.json +7 -7
- package/src/content/.agents/skills/browser-automation/SKILL.md +0 -66
- package/src/presets/browser.json +0 -22
- package/src/steps/browser/index.js +0 -91
- /package/{src → cli}/commands/shared.js +0 -0
- /package/{src → cli}/commands/single.js +0 -0
- /package/{src → cli}/commands/update.js +0 -0
- /package/{src → cli}/fragments/archive/az.md +0 -0
- /package/{src → cli}/fragments/archive/gh.md +0 -0
- /package/{src → cli}/fragments/archive/gl.md +0 -0
- /package/{src → cli}/fragments/archive/none.md +0 -0
- /package/{src → cli}/fragments/guardrails/codegraph.md +0 -0
- /package/{src → cli}/fragments/guardrails/humanizer.md +0 -0
- /package/{src → cli}/fragments/guardrails/memory.md +0 -0
- /package/{src → cli}/fragments/guardrails/rtk.md +0 -0
- /package/{src → cli}/fragments/guardrails/simple-english.md +0 -0
- /package/{src → cli}/fragments/ops-backlog/az.md +0 -0
- /package/{src → cli}/fragments/ops-backlog/gh.md +0 -0
- /package/{src → cli}/fragments/ops-backlog/jira.md +0 -0
- /package/{src → cli}/fragments/ops-evidence/az.md +0 -0
- /package/{src → cli}/fragments/ops-evidence/gh.md +0 -0
- /package/{src → cli}/fragments/ops-evidence/jira.md +0 -0
- /package/{src → cli}/fragments/ops-review/az.md +0 -0
- /package/{src → cli}/fragments/ops-review/gh.md +0 -0
- /package/{src → cli}/fragments/ops-review/gl.md +0 -0
- /package/{src → cli}/fragments/ops-ship/az.md +0 -0
- /package/{src → cli}/fragments/ops-ship/gh.md +0 -0
- /package/{src → cli}/fragments/ops-ship/gl.md +0 -0
- /package/{src → cli}/presets/models.json +0 -0
- /package/{src → cli}/presets/openspec.json +0 -0
- /package/{src → cli}/steps/metadata/index.js +0 -0
- /package/{src → cli}/steps/models/write.js +0 -0
- /package/{src → cli}/utils/copy.js +0 -0
- /package/{src → cli}/utils/legacy-check.js +0 -0
- /package/{src → cli}/utils/models-cache.js +0 -0
- /package/{src → cli}/utils/update-manifest.js +0 -0
- /package/{src/content → harness}/.agents/skills/pc-guardrails-generic/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-guardrails-project/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-architecture/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-architecture/structure-template.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-design/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-engineer/signal-mapping.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-evidence-scaffold/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-evidence-scaffold/evidence-contract.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-guardrails/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-guardrails/category-reference.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-merge-risk-assess/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-merge-risk-assess/category-reference.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-make-user-model/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-ops-evidence/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-ops-ship/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-apply/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-apply/simple-mode.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-goal/failure-policy.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-goal/output-mode.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-propose/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-propose/task-annotation.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-plan-quick/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-repo-audit/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-repo-help/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-repo-initialize/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-repo-onboard/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-repo-verify/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-userstory-az/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-userstory-gh/SKILL.md +0 -0
- /package/{src/content → harness}/.agents/skills/pc-userstory-jira/SKILL.md +0 -0
- /package/{src/content → harness}/.opencode/_gitignore +0 -0
- /package/{src/content → harness}/.opencode/commands/init.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-architecture.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-design.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-engineer.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-evidence-scaffold.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-guardrails.md +0 -0
- /package/{src/content → harness}/.opencode/commands/make-user-model.md +0 -0
- /package/{src/content → harness}/.opencode/commands/ops-backlog.md +0 -0
- /package/{src/content → harness}/.opencode/commands/ops-evidence.md +0 -0
- /package/{src/content → harness}/.opencode/commands/ops-review.md +0 -0
- /package/{src/content → harness}/.opencode/commands/ops-ship.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-apply.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-archive.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-explore.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-goal.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-propose.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-quick.md +0 -0
- /package/{src/content → harness}/.opencode/commands/plan-story.md +0 -0
- /package/{src/content → harness}/.opencode/commands/repo-audit.md +0 -0
- /package/{src/content → harness}/.opencode/commands/repo-help.md +0 -0
- /package/{src/content → harness}/.opencode/commands/repo-initialize.md +0 -0
- /package/{src/content → harness}/.opencode/commands/repo-onboard.md +0 -0
- /package/{src/content → harness}/.opencode/commands/repo-verify.md +0 -0
- /package/{src/content → harness}/.opencode/plugins/pc-subagent-monitor.js +0 -0
- /package/{src/content → harness}/.opencode/plugins/pc-system-reminders.js +0 -0
- /package/{src/content → harness}/.opencode/tui/pc-subagents.tsx +0 -0
- /package/{src/content → harness}/.opencode/tui.json +0 -0
- /package/{src/content → harness}/AGENTS.md +0 -0
- /package/{src/content → harness}/openspec/changes/archive/.gitkeep +0 -0
- /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
|
-
|
|
66
|
-
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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, "
|
|
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")
|