@plainconceptsplatform/agent-harness 3.3.0 → 3.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 (95) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +454 -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 -307
  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 -80
  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 -9
  52. package/harness/.opencode/commands/ops-backlog.md +10 -10
  53. package/harness/.opencode/commands/ops-review.md +10 -8
  54. package/harness/.opencode/commands/ops-ship.md +9 -9
  55. package/harness/.opencode/commands/plan-archive.md +7 -5
  56. package/harness/.opencode/commands/plan-explore.md +11 -9
  57. package/harness/.opencode/commands/plan-propose.md +11 -9
  58. package/harness/.opencode/commands/plan-quick.md +7 -5
  59. package/harness/.opencode/commands/plan-story.md +11 -9
  60. package/harness/.opencode/commands/repo-audit.md +7 -5
  61. package/harness/.opencode/commands/repo-help.md +5 -5
  62. package/harness/.opencode/package.json +5 -5
  63. package/harness/.opencode/plugins/pc-subagent-monitor.js +184 -184
  64. package/harness/.opencode/plugins/pc-subagent-tiers.js +367 -354
  65. package/harness/.opencode/plugins/pc-system-reminders.js +386 -386
  66. package/harness/.opencode/scripts/check-diff.mjs +244 -244
  67. package/harness/.opencode/scripts/code-rules.mjs +288 -288
  68. package/harness/ARCHITECTURE.md +13 -13
  69. package/harness/DESIGN.md +13 -13
  70. package/harness/opencode.jsonc +36 -36
  71. package/harness/openspec/config.yaml +20 -20
  72. package/harness/skills-lock.json +17 -17
  73. package/package.json +12 -14
  74. package/skills/agent-harness-cli/SKILL.md +86 -86
  75. package/skills/pc-guardrails-generic/SKILL.md +53 -53
  76. package/skills/pc-guardrails-project/SKILL.md +8 -8
  77. package/skills/pc-make-architecture/structure-template.md +64 -64
  78. package/skills/pc-make-engineer/SKILL.md +59 -59
  79. package/skills/pc-make-engineer/template.md +36 -36
  80. package/skills/pc-make-guardrails/category-reference.md +112 -112
  81. package/skills/pc-make-merge-risk-assess/SKILL.md +70 -70
  82. package/skills/pc-make-merge-risk-assess/category-reference.md +114 -114
  83. package/skills/pc-ops-ship/SKILL.md +22 -22
  84. package/skills/pc-plan-apply/SKILL.md +86 -86
  85. package/skills/pc-plan-apply/simple-mode.md +21 -21
  86. package/skills/pc-plan-explore/SKILL.md +21 -21
  87. package/skills/pc-plan-goal/SKILL.md +91 -91
  88. package/skills/pc-plan-goal/failure-policy.md +30 -30
  89. package/skills/pc-plan-goal/output-mode.md +10 -10
  90. package/skills/pc-plan-propose/SKILL.md +125 -125
  91. package/skills/pc-plan-propose/task-annotation.md +39 -39
  92. package/skills/pc-plan-quick/SKILL.md +46 -46
  93. package/skills/pc-repo-audit/SKILL.md +44 -44
  94. package/skills/pc-repo-help/SKILL.md +87 -87
  95. package/skills/pc-repo-verify/SKILL.md +150 -150
@@ -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
+ }
@@ -1,79 +1,79 @@
1
- import fse from 'fs-extra'
2
- import path from 'node:path'
3
- import { fileURLToPath } from 'url'
4
- import { cleanAiFiles } from '../steps/clean/index.js'
5
- import { copyContentStep } from '../steps/copy/index.js'
6
- import { chooseModels } from '../steps/models/index.js'
7
- import { initOpenspec } from '../steps/openspec/index.js'
8
- import { tokenOptimizationStep } from '../steps/optimization/index.js'
9
- import { choosePlatform } from '../steps/platform/index.js'
10
- import { installBrowser } from '../steps/browser/index.js'
11
- import { writeHarnessConfig } from '../steps/metadata/index.js'
12
- import { readHarnessConfig } from './shared.js'
13
-
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
15
- const platformsPreset = await fse.readJson(path.resolve(__dirname, '../presets/platforms.json'))
16
- // platforms.json is the single source of truth for valid platform values.
17
- // Hardcoding a subset here silently rewrote jira/gitlab/browser projects to github.
18
- const VALID_PLATFORMS = new Set(platformsPreset.map(p => p.value))
19
-
20
- export function resolvePlatform(value, fallback = 'github') {
21
- return VALID_PLATFORMS.has(value) ? value : fallback
22
- }
23
-
24
- export async function runSingleCommand(command) {
25
- const saved = await readHarnessConfig()
26
- const ctx = {
27
- hasDesign: !!saved?.preexisting?.design,
28
- hasArchitecture: !!saved?.preexisting?.architecture,
29
- hasOpenspec: !!saved?.preexisting?.openspec,
30
- sourceMode: saved?.source?.mode ?? 'current',
31
- sourceRoots: Array.isArray(saved?.source?.roots) ? saved.source.roots : [],
32
- maxConcurrentAgents: saved?.agents?.maxConcurrent ?? 3,
33
- }
34
- const backlogPlatform = saved?.platform?.backlog
35
- const repoPlatform = saved?.platform?.repo
36
- const resolvedBacklog = resolvePlatform(backlogPlatform)
37
- const resolvedRepo = resolvePlatform(repoPlatform)
38
-
39
- const handlers = {
40
- clean: async () => {
41
- await cleanAiFiles()
42
- },
43
- platform: async () => {
44
- await choosePlatform()
45
- },
46
- copy: async () => {
47
- await copyContentStep({ backlogPlatform: resolvedBacklog, repoPlatform: resolvedRepo }, ctx)
48
- },
49
- openspec: async () => {
50
- await initOpenspec()
51
- },
52
- models: async () => {
53
- await chooseModels()
54
- },
55
- optimization: async () => {
56
- await tokenOptimizationStep({ ctx })
57
- },
58
- browser: async () => {
59
- await installBrowser()
60
- },
61
- metadata: async () => {
62
- await writeHarnessConfig({
63
- ...ctx,
64
- backlogPlatform: resolvedBacklog,
65
- repoPlatform: resolvedRepo,
66
- maxConcurrentAgents: saved?.agents?.maxConcurrent ?? 3,
67
- planModel: saved?.models?.plan ?? null,
68
- buildModel: saved?.models?.build ?? null,
69
- fastModel: saved?.models?.fast ?? null,
70
- optionalTools: saved?.tools ?? null,
71
- })
72
- },
73
- }
74
-
75
- const handler = handlers[command]
76
- if (!handler) return false
77
- await handler()
78
- return true
79
- }
1
+ import fse from 'fs-extra'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'url'
4
+ import { cleanAiFiles } from '../steps/clean/index.js'
5
+ import { copyContentStep } from '../steps/copy/index.js'
6
+ import { chooseModels } from '../steps/models/index.js'
7
+ import { initOpenspec } from '../steps/openspec/index.js'
8
+ import { tokenOptimizationStep } from '../steps/optimization/index.js'
9
+ import { choosePlatform } from '../steps/platform/index.js'
10
+ import { installBrowser } from '../steps/browser/index.js'
11
+ import { writeHarnessConfig } from '../steps/metadata/index.js'
12
+ import { readHarnessConfig } from './shared.js'
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
15
+ const platformsPreset = await fse.readJson(path.resolve(__dirname, '../presets/platforms.json'))
16
+ // platforms.json is the single source of truth for valid platform values.
17
+ // Hardcoding a subset here silently rewrote jira/gitlab/browser projects to github.
18
+ const VALID_PLATFORMS = new Set(platformsPreset.map(p => p.value))
19
+
20
+ export function resolvePlatform(value, fallback = 'github') {
21
+ return VALID_PLATFORMS.has(value) ? value : fallback
22
+ }
23
+
24
+ export async function runSingleCommand(command) {
25
+ const saved = await readHarnessConfig()
26
+ const ctx = {
27
+ hasDesign: !!saved?.preexisting?.design,
28
+ hasArchitecture: !!saved?.preexisting?.architecture,
29
+ hasOpenspec: !!saved?.preexisting?.openspec,
30
+ sourceMode: saved?.source?.mode ?? 'current',
31
+ sourceRoots: Array.isArray(saved?.source?.roots) ? saved.source.roots : [],
32
+ maxConcurrentAgents: saved?.agents?.maxConcurrent ?? 3,
33
+ }
34
+ const backlogPlatform = saved?.platform?.backlog
35
+ const repoPlatform = saved?.platform?.repo
36
+ const resolvedBacklog = resolvePlatform(backlogPlatform)
37
+ const resolvedRepo = resolvePlatform(repoPlatform)
38
+
39
+ const handlers = {
40
+ clean: async () => {
41
+ await cleanAiFiles()
42
+ },
43
+ platform: async () => {
44
+ await choosePlatform()
45
+ },
46
+ copy: async () => {
47
+ await copyContentStep({ backlogPlatform: resolvedBacklog, repoPlatform: resolvedRepo }, ctx)
48
+ },
49
+ openspec: async () => {
50
+ await initOpenspec()
51
+ },
52
+ models: async () => {
53
+ await chooseModels()
54
+ },
55
+ optimization: async () => {
56
+ await tokenOptimizationStep({ ctx })
57
+ },
58
+ browser: async () => {
59
+ await installBrowser()
60
+ },
61
+ metadata: async () => {
62
+ await writeHarnessConfig({
63
+ ...ctx,
64
+ backlogPlatform: resolvedBacklog,
65
+ repoPlatform: resolvedRepo,
66
+ maxConcurrentAgents: saved?.agents?.maxConcurrent ?? 3,
67
+ planModel: saved?.models?.plan ?? null,
68
+ buildModel: saved?.models?.build ?? null,
69
+ fastModel: saved?.models?.fast ?? null,
70
+ optionalTools: saved?.tools ?? null,
71
+ })
72
+ },
73
+ }
74
+
75
+ const handler = handlers[command]
76
+ if (!handler) return false
77
+ await handler()
78
+ return true
79
+ }
@@ -1,134 +1,134 @@
1
- import { select as wizardSelect } from '@inquirer/prompts'
2
- import chalk from 'chalk'
3
- import { ensureGitLongpaths } from './shared.js'
4
- import { chooseSourceScope } from '../steps/source/index.js'
5
- import { cleanAiFiles } from '../steps/clean/index.js'
6
- import { choosePlatform } from '../steps/platform/index.js'
7
- import { copyContentStep } from '../steps/copy/index.js'
8
- import { initOpenspec } from '../steps/openspec/index.js'
9
- import { chooseModels } from '../steps/models/index.js'
10
- import { tokenOptimizationStep } from '../steps/optimization/index.js'
11
- import { installBrowser } from '../steps/browser/index.js'
12
- import { writeHarnessConfig } from '../steps/metadata/index.js'
13
-
14
- export async function runWizard(version) {
15
- const logo = chalk.hex('#fe3d57')
16
- const bannerLines = [
17
- logo(' '),
18
- logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
19
- logo(' ▒▒▓ ▓▒▓ '),
20
- logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
21
- logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
22
- logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
23
- logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
24
- logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
25
- logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
26
- logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
27
- logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
28
- logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
29
- '',
30
- chalk.bold(' 🧰 agent-harness') + chalk.dim(` v${version}`),
31
- chalk.dim(' Prepare your codebase for AI agents'),
32
- ]
33
-
34
- for (const line of bannerLines) console.log(line)
35
- console.log()
36
- console.log(' This tool will set up your project with a team of AI agents,')
37
- console.log(' install skills, select models, and configure OpenCode.')
38
- console.log()
39
-
40
- // Only wait for Enter in a real interactive TTY
41
- if (process.stdin.isTTY) {
42
- console.log(chalk.bold(' Press Enter to begin...'))
43
- console.log()
44
- await new Promise(resolve => {
45
- process.stdin.resume()
46
- process.stdin.once('data', () => {
47
- process.stdin.pause()
48
- resolve()
49
- })
50
- })
51
- }
52
-
53
- const longpathsConfigured = await ensureGitLongpaths()
54
- if (longpathsConfigured) console.log(chalk.dim(' Git long paths enabled for this repository.'))
55
-
56
- const scope = await chooseSourceScope()
57
-
58
- const maxConcurrentAgents = await wizardSelect({
59
- message: 'Max concurrent agents:',
60
- default: 3,
61
- choices: [
62
- { name: '1', value: 1, description: 'Serial: one agent at a time' },
63
- { name: '2', value: 2, description: 'Conservative: lower resource usage' },
64
- { name: '3 (default)', value: 3, description: 'Recommended: balances speed vs MCP load' },
65
- { name: '4', value: 4, description: 'Higher parallelism' },
66
- { name: '5', value: 5, description: 'Maximum: needs more resources' },
67
- ],
68
- })
69
-
70
- const installScope = await wizardSelect({
71
- message: 'Install dependencies:',
72
- default: 'local',
73
- choices: [
74
- { name: 'Locally (default)', value: 'local', description: 'Install tools project-locally where possible' },
75
- { name: 'Globally', value: 'global', description: 'Install tools globally (affects all projects on this machine)' },
76
- ],
77
- })
78
-
79
- const preserve = await cleanAiFiles({ removeLegacyConfig: true })
80
- const ctx = { ...preserve, ...scope, maxConcurrentAgents, installScope }
81
-
82
- const { backlogPlatform, repoPlatform } = await choosePlatform()
83
-
84
- await copyContentStep({ backlogPlatform, repoPlatform }, ctx)
85
-
86
- const openspec = await initOpenspec()
87
-
88
- const selectedModels = await chooseModels()
89
-
90
- const tokenOpt = await tokenOptimizationStep({ ctx })
91
- const { rtk, quota, simpleEnglish, codegraph, memory, humanizer } = tokenOpt
92
-
93
- await installBrowser()
94
-
95
- await writeHarnessConfig({
96
- ...ctx,
97
- backlogPlatform,
98
- repoPlatform,
99
- openspec,
100
- maxConcurrentAgents,
101
- installScope,
102
- additionalSkillsProvider: 'npx-skills',
103
- ...selectedModels,
104
- optionalTools: { rtk, quota, simpleEnglish, codegraph, memory, humanizer },
105
- })
106
-
107
- const toGenerate = [
108
- !ctx.hasDesign && 'DESIGN.md',
109
- !ctx.hasArchitecture && 'ARCHITECTURE.md',
110
- ].filter(Boolean)
111
-
112
- console.log()
113
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
114
- console.log(chalk.bold.green(' Onboarding complete!'))
115
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
116
- console.log()
117
- console.log(' Open this project in OpenCode and type:')
118
- console.log(chalk.bold(' "/init"'))
119
- console.log(' Or run the following command in your terminal:')
120
- console.log(chalk.bold(' opencode --prompt "/init" --model "opencode/big-pickle" '))
121
- console.log()
122
- console.log(chalk.dim(' Note: the first time you open OpenCode in this project,'))
123
- console.log(chalk.dim(' it will install plugin dependencies. You may see a black'))
124
- console.log(chalk.dim(' screen for a moment: this is normal. Wait for it to load.'))
125
- console.log()
126
- if (toGenerate.length > 0) {
127
- console.log(` OpenCode will generate ${toGenerate.join(' and ')}`)
128
- console.log(' from your actual codebase, then activate the agent team.')
129
- } else {
130
- console.log(' OpenCode will activate the agent team.')
131
- }
132
- console.log(` Source scope: ${ctx.sourceMode === 'parent-selected' ? ctx.sourceRoots.map(p => `../${p.split(/[/\\]/).pop()}`).join(', ') : 'current folder'}`)
133
- console.log()
134
- }
1
+ import { select as wizardSelect } from '@inquirer/prompts'
2
+ import chalk from 'chalk'
3
+ import { ensureGitLongpaths } from './shared.js'
4
+ import { chooseSourceScope } from '../steps/source/index.js'
5
+ import { cleanAiFiles } from '../steps/clean/index.js'
6
+ import { choosePlatform } from '../steps/platform/index.js'
7
+ import { copyContentStep } from '../steps/copy/index.js'
8
+ import { initOpenspec } from '../steps/openspec/index.js'
9
+ import { chooseModels } from '../steps/models/index.js'
10
+ import { tokenOptimizationStep } from '../steps/optimization/index.js'
11
+ import { installBrowser } from '../steps/browser/index.js'
12
+ import { writeHarnessConfig } from '../steps/metadata/index.js'
13
+
14
+ export async function runWizard(version) {
15
+ const logo = chalk.hex('#fe3d57')
16
+ const bannerLines = [
17
+ logo(' '),
18
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
19
+ logo(' ▒▒▓ ▓▒▓ '),
20
+ logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
21
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
22
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
23
+ logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
24
+ logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
25
+ logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
26
+ logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
27
+ logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
28
+ logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
29
+ '',
30
+ chalk.bold(' 🧰 agent-harness') + chalk.dim(` v${version}`),
31
+ chalk.dim(' Prepare your codebase for AI agents'),
32
+ ]
33
+
34
+ for (const line of bannerLines) console.log(line)
35
+ console.log()
36
+ console.log(' This tool will set up your project with a team of AI agents,')
37
+ console.log(' install skills, select models, and configure OpenCode.')
38
+ console.log()
39
+
40
+ // Only wait for Enter in a real interactive TTY
41
+ if (process.stdin.isTTY) {
42
+ console.log(chalk.bold(' Press Enter to begin...'))
43
+ console.log()
44
+ await new Promise(resolve => {
45
+ process.stdin.resume()
46
+ process.stdin.once('data', () => {
47
+ process.stdin.pause()
48
+ resolve()
49
+ })
50
+ })
51
+ }
52
+
53
+ const longpathsConfigured = await ensureGitLongpaths()
54
+ if (longpathsConfigured) console.log(chalk.dim(' Git long paths enabled for this repository.'))
55
+
56
+ const scope = await chooseSourceScope()
57
+
58
+ const maxConcurrentAgents = await wizardSelect({
59
+ message: 'Max concurrent agents:',
60
+ default: 3,
61
+ choices: [
62
+ { name: '1', value: 1, description: 'Serial: one agent at a time' },
63
+ { name: '2', value: 2, description: 'Conservative: lower resource usage' },
64
+ { name: '3 (default)', value: 3, description: 'Recommended: balances speed vs MCP load' },
65
+ { name: '4', value: 4, description: 'Higher parallelism' },
66
+ { name: '5', value: 5, description: 'Maximum: needs more resources' },
67
+ ],
68
+ })
69
+
70
+ const installScope = await wizardSelect({
71
+ message: 'Install dependencies:',
72
+ default: 'local',
73
+ choices: [
74
+ { name: 'Locally (default)', value: 'local', description: 'Install tools project-locally where possible' },
75
+ { name: 'Globally', value: 'global', description: 'Install tools globally (affects all projects on this machine)' },
76
+ ],
77
+ })
78
+
79
+ const preserve = await cleanAiFiles({ removeLegacyConfig: true })
80
+ const ctx = { ...preserve, ...scope, maxConcurrentAgents, installScope }
81
+
82
+ const { backlogPlatform, repoPlatform } = await choosePlatform()
83
+
84
+ await copyContentStep({ backlogPlatform, repoPlatform }, ctx)
85
+
86
+ const openspec = await initOpenspec()
87
+
88
+ const selectedModels = await chooseModels()
89
+
90
+ const tokenOpt = await tokenOptimizationStep({ ctx })
91
+ const { rtk, quota, simpleEnglish, codegraph, memory, humanizer } = tokenOpt
92
+
93
+ await installBrowser()
94
+
95
+ await writeHarnessConfig({
96
+ ...ctx,
97
+ backlogPlatform,
98
+ repoPlatform,
99
+ openspec,
100
+ maxConcurrentAgents,
101
+ installScope,
102
+ additionalSkillsProvider: 'npx-skills',
103
+ ...selectedModels,
104
+ optionalTools: { rtk, quota, simpleEnglish, codegraph, memory, humanizer },
105
+ })
106
+
107
+ const toGenerate = [
108
+ !ctx.hasDesign && 'DESIGN.md',
109
+ !ctx.hasArchitecture && 'ARCHITECTURE.md',
110
+ ].filter(Boolean)
111
+
112
+ console.log()
113
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
114
+ console.log(chalk.bold.green(' Onboarding complete!'))
115
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
116
+ console.log()
117
+ console.log(' Open this project in OpenCode and type:')
118
+ console.log(chalk.bold(' "/init"'))
119
+ console.log(' Or run the following command in your terminal:')
120
+ console.log(chalk.bold(' opencode --prompt "/init" --model "opencode/big-pickle" '))
121
+ console.log()
122
+ console.log(chalk.dim(' Note: the first time you open OpenCode in this project,'))
123
+ console.log(chalk.dim(' it will install plugin dependencies. You may see a black'))
124
+ console.log(chalk.dim(' screen for a moment: this is normal. Wait for it to load.'))
125
+ console.log()
126
+ if (toGenerate.length > 0) {
127
+ console.log(` OpenCode will generate ${toGenerate.join(' and ')}`)
128
+ console.log(' from your actual codebase, then activate the agent team.')
129
+ } else {
130
+ console.log(' OpenCode will activate the agent team.')
131
+ }
132
+ console.log(` Source scope: ${ctx.sourceMode === 'parent-selected' ? ctx.sourceRoots.map(p => `../${p.split(/[/\\]/).pop()}`).join(', ') : 'current folder'}`)
133
+ console.log()
134
+ }
@@ -1,29 +1,29 @@
1
- Azure DevOps data comes from the `az` CLI; a page fetch of dev.azure.com is denied (pc-system-reminders). If `az` is unavailable, report it as a blocker.
2
-
3
- ---
4
-
5
- ### Step 1: Parse input
6
-
7
- `$ARGUMENTS` is the work item title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
-
9
- ### Step 2: Create work item
10
-
11
- ```bash
12
- az boards work-item create \
13
- --title "{title}" \
14
- --description "{body}" \
15
- --type "User Story"
16
- ```
17
-
18
- ### Step 3: Report
19
-
20
- ```text
21
- Work item created
22
- ID: {id}
23
- Title: {title}
24
- URL: {work-item-url}
25
- ```
26
-
27
- Tell the user: "Use `/plan-propose {work-item-url}` to turn this into a plan."
28
-
1
+ Azure DevOps data comes from the `az` CLI; a page fetch of dev.azure.com is denied (pc-system-reminders). If `az` is unavailable, report it as a blocker.
2
+
3
+ ---
4
+
5
+ ### Step 1: Parse input
6
+
7
+ `$ARGUMENTS` is the work item title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
+
9
+ ### Step 2: Create work item
10
+
11
+ ```bash
12
+ az boards work-item create \
13
+ --title "{title}" \
14
+ --description "{body}" \
15
+ --type "User Story"
16
+ ```
17
+
18
+ ### Step 3: Report
19
+
20
+ ```text
21
+ Work item created
22
+ ID: {id}
23
+ Title: {title}
24
+ URL: {work-item-url}
25
+ ```
26
+
27
+ Tell the user: "Use `/plan-propose {work-item-url}` to turn this into a plan."
28
+
29
29
  ---
@@ -1,29 +1,29 @@
1
- GitHub data comes from the `gh` CLI; a page fetch of github.com is denied (pc-system-reminders). If `gh` is unavailable, report it as a blocker. Always pass `--repo {owner}/{repo}` explicitly, never rely on git context to resolve the repo.
2
-
3
- ---
4
-
5
- ### Step 1: Parse input
6
-
7
- `$ARGUMENTS` is the issue title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
-
9
- ### Step 2: Create issue
10
-
11
- ```bash
12
- gh issue create \
13
- --repo {owner}/{repo} \
14
- --title "{title}" \
15
- --body "{body}"
16
- ```
17
-
18
- ### Step 3: Report
19
-
20
- ```text
21
- Issue created
22
- URL: {issue-url}
23
- Number: #{number}
24
- Title: {title}
25
- ```
26
-
27
- Tell the user: "Use `/plan-propose {issue-url}` to turn this into a plan."
28
-
1
+ GitHub data comes from the `gh` CLI; a page fetch of github.com is denied (pc-system-reminders). If `gh` is unavailable, report it as a blocker. Always pass `--repo {owner}/{repo}` explicitly, never rely on git context to resolve the repo.
2
+
3
+ ---
4
+
5
+ ### Step 1: Parse input
6
+
7
+ `$ARGUMENTS` is the issue title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
+
9
+ ### Step 2: Create issue
10
+
11
+ ```bash
12
+ gh issue create \
13
+ --repo {owner}/{repo} \
14
+ --title "{title}" \
15
+ --body "{body}"
16
+ ```
17
+
18
+ ### Step 3: Report
19
+
20
+ ```text
21
+ Issue created
22
+ URL: {issue-url}
23
+ Number: #{number}
24
+ Title: {title}
25
+ ```
26
+
27
+ Tell the user: "Use `/plan-propose {issue-url}` to turn this into a plan."
28
+
29
29
  ---
@@ -1,29 +1,29 @@
1
- Jira data comes from the `acli` CLI; a page fetch of atlassian.net is denied (pc-system-reminders). If `acli` is unavailable, report it as a blocker.
2
-
3
- ---
4
-
5
- ### Step 1: Parse input
6
-
7
- `$ARGUMENTS` is the issue title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
-
9
- ### Step 2: Create issue
10
-
11
- ```bash
12
- acli jira issue create \
13
- --summary "{title}" \
14
- --description "{body}" \
15
- --type "Story"
16
- ```
17
-
18
- ### Step 3: Report
19
-
20
- ```text
21
- Issue created
22
- Key: {key}
23
- Title: {title}
24
- URL: {issue-url}
25
- ```
26
-
27
- Tell the user: "Use `/plan-propose {issue-url}` to turn this into a plan."
28
-
1
+ Jira data comes from the `acli` CLI; a page fetch of atlassian.net is denied (pc-system-reminders). If `acli` is unavailable, report it as a blocker.
2
+
3
+ ---
4
+
5
+ ### Step 1: Parse input
6
+
7
+ `$ARGUMENTS` is the issue title/description. If it contains a title and body separated by a newline or `---`, split them. Otherwise use the full text as the title with an empty body.
8
+
9
+ ### Step 2: Create issue
10
+
11
+ ```bash
12
+ acli jira issue create \
13
+ --summary "{title}" \
14
+ --description "{body}" \
15
+ --type "Story"
16
+ ```
17
+
18
+ ### Step 3: Report
19
+
20
+ ```text
21
+ Issue created
22
+ Key: {key}
23
+ Title: {title}
24
+ URL: {issue-url}
25
+ ```
26
+
27
+ Tell the user: "Use `/plan-propose {issue-url}` to turn this into a plan."
28
+
29
29
  ---