opencode-onboard 0.4.3 → 0.4.4
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 +41 -40
- package/content/.agents/agents/devops-manager.md +123 -123
- package/content/.agents/skills/ob-default/SKILL.md +25 -21
- package/content/.agents/skills/ob-generic-guardrails/SKILL.md +36 -32
- package/content/.agents/skills/ob-global/SKILL.md +92 -84
- package/content/.agents/skills/ob-pullrequest-az/SKILL.md +168 -160
- package/content/.agents/skills/ob-pullrequest-gh/SKILL.md +140 -136
- package/content/.opencode/commands/create-engineer.md +109 -0
- package/content/.opencode/plugins/session-log.js +523 -519
- package/content/AGENTS.md +23 -21
- package/package.json +1 -1
- package/src/commands/wizard.js +124 -113
- package/src/presets/browser.json +22 -18
- package/src/presets/optimization.json +27 -22
- package/src/steps/browser/browser.test.js +115 -81
- package/src/steps/browser/index.js +62 -54
- package/src/steps/clean/index.js +108 -107
- package/src/steps/metadata/index.js +63 -62
- package/src/steps/models/format.js +61 -60
- package/src/steps/models/write.test.js +117 -117
- package/src/steps/openspec/ensemble.test.js +79 -79
- package/src/steps/openspec/index.js +121 -32
- package/src/steps/openspec/index.test.js +63 -0
- package/src/steps/optimization/caveman.js +34 -29
- package/src/steps/optimization/codegraph.js +52 -0
- package/src/steps/optimization/global.js +88 -64
- package/src/steps/optimization/global.test.js +99 -0
- package/src/steps/optimization/index.js +109 -101
- package/src/steps/optimization/optimization.test.js +101 -93
- package/src/steps/optimization/quota.js +84 -84
- package/src/steps/source/source.test.js +124 -124
- package/src/utils/__tests__/copy.test.js +117 -117
- package/src/utils/exec-spinner.js +47 -47
- package/src/utils/exec.js +134 -131
- package/src/utils/terminal.js +6 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
import { header, success, warn, error, loading } from '../../utils/exec.js'
|
|
3
|
+
|
|
4
|
+
export async function installCodegraph(options = {}) {
|
|
5
|
+
if (!options.skipHeader) header('Installing codegraph')
|
|
6
|
+
|
|
7
|
+
const location = options.installScope === 'global' ? 'global' : 'local'
|
|
8
|
+
|
|
9
|
+
loading(`configuring codegraph for opencode (${location})...`)
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const installResult = await execa(
|
|
13
|
+
'npx',
|
|
14
|
+
['@colbymchenry/codegraph', 'install', '--target=opencode', `--location=${location}`, '--yes'],
|
|
15
|
+
{
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
reject: false,
|
|
18
|
+
stdio: 'pipe',
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if (installResult.exitCode !== 0) {
|
|
23
|
+
warn('codegraph install exited with non-zero code')
|
|
24
|
+
return { optedIn: true, installed: false }
|
|
25
|
+
}
|
|
26
|
+
success(`codegraph configured for opencode (${location})`)
|
|
27
|
+
} catch (err) {
|
|
28
|
+
error(`Failed to install codegraph: ${err.message}`)
|
|
29
|
+
return { optedIn: true, installed: false }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
loading('initializing codegraph project index...')
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const initResult = await execa('codegraph', ['init'], {
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
reject: false,
|
|
38
|
+
stdio: 'pipe',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
if (initResult.exitCode !== 0) {
|
|
42
|
+
warn('codegraph init exited with non-zero code')
|
|
43
|
+
return { optedIn: true, installed: false }
|
|
44
|
+
}
|
|
45
|
+
success('codegraph project index initialized')
|
|
46
|
+
} catch (err) {
|
|
47
|
+
error(`Failed to initialize codegraph: ${err.message}`)
|
|
48
|
+
return { optedIn: true, installed: false }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { optedIn: true, installed: true }
|
|
52
|
+
}
|
|
@@ -1,64 +1,88 @@
|
|
|
1
|
-
import fse from 'fs-extra'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { info, success, warn } from '../../utils/exec.js'
|
|
4
|
-
|
|
5
|
-
const SOURCE_START = '<!-- OB-SOURCE-ROOTS-START -->'
|
|
6
|
-
const SOURCE_END = '<!-- OB-SOURCE-ROOTS-END -->'
|
|
7
|
-
const RTK_START = '<!-- OB-RTK-START -->'
|
|
8
|
-
const RTK_END = '<!-- OB-RTK-END -->'
|
|
9
|
-
const CAVEMAN_START = '<!-- OB-CAVEMAN-START -->'
|
|
10
|
-
const CAVEMAN_END = '<!-- OB-CAVEMAN-END -->'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
import fse from 'fs-extra'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { info, success, warn } from '../../utils/exec.js'
|
|
4
|
+
|
|
5
|
+
const SOURCE_START = '<!-- OB-SOURCE-ROOTS-START -->'
|
|
6
|
+
const SOURCE_END = '<!-- OB-SOURCE-ROOTS-END -->'
|
|
7
|
+
const RTK_START = '<!-- OB-RTK-START -->'
|
|
8
|
+
const RTK_END = '<!-- OB-RTK-END -->'
|
|
9
|
+
const CAVEMAN_START = '<!-- OB-CAVEMAN-START -->'
|
|
10
|
+
const CAVEMAN_END = '<!-- OB-CAVEMAN-END -->'
|
|
11
|
+
|
|
12
|
+
const CODEGRAPH_START = '<!-- OB-CODEGRAPH-START -->'
|
|
13
|
+
const CODEGRAPH_END = '<!-- OB-CODEGRAPH-END -->'
|
|
14
|
+
|
|
15
|
+
function relRoot(cwd, abs) {
|
|
16
|
+
const rel = path.relative(cwd, abs).replace(/\\/g, '/')
|
|
17
|
+
return rel || '.'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function buildSourceRootsSection(sourceRoots, cwd) {
|
|
21
|
+
const roots = Array.isArray(sourceRoots) && sourceRoots.length > 0 ? sourceRoots : [cwd]
|
|
22
|
+
const bullets = roots.map(r => `- \`${relRoot(cwd, r)}\``).join('\n')
|
|
23
|
+
const multiRepo = roots.length > 1
|
|
24
|
+
? '\nEach root is an independent git repository. For branch/commit/push workflows, run git operations per repository. There is no single shared git history across all roots.\n'
|
|
25
|
+
: ''
|
|
26
|
+
|
|
27
|
+
return `Read and analyze code ONLY from these roots:\n\n${bullets}\n${multiRepo}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildRtkSection(rtkEnabled) {
|
|
31
|
+
if (!rtkEnabled) return 'RTK was not selected during onboarding. Do not assume `rtk` exists.'
|
|
32
|
+
return `## RTK, MANDATORY\n\nUse \`rtk\` for ALL CLI commands. Never run commands directly.\n\n- \`rtk git\` NOT \`git\`\n- \`rtk gh\` NOT \`gh\`\n- \`rtk az\` NOT \`az\`\n- \`rtk openspec\` NOT \`openspec\`\n\nIf \`rtk\` is not available, report blocker and stop CLI execution.`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildCavemanSection(cavemanEnabled) {
|
|
36
|
+
if (!cavemanEnabled) return 'Caveman was not selected during onboarding. Use normal concise style.'
|
|
37
|
+
return `## Caveman\n\ncaveman mode. Active now. Every response. No revert unless user asks "stop caveman" or "normal mode".`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildCodegraphSection(codegraphEnabled) {
|
|
41
|
+
if (!codegraphEnabled) return 'Codegraph was not selected during onboarding. Use standard grep/glob/read for code exploration.'
|
|
42
|
+
return `## CodeGraph
|
|
43
|
+
|
|
44
|
+
This project has CodeGraph initialized (\`.codegraph/\` exists). Use it for all code exploration.
|
|
45
|
+
|
|
46
|
+
**NEVER call \`codegraph_explore\` or \`codegraph_context\` directly in the main session** — these return large source payloads that fill context. Instead, ALWAYS spawn an Explore sub-agent for exploration questions ("how does X work?", "where is Y implemented?").
|
|
47
|
+
|
|
48
|
+
When spawning Explore agents, include in the prompt:
|
|
49
|
+
> This project has CodeGraph initialized. Use \`codegraph_explore\` as your PRIMARY tool. Do NOT re-read files that codegraph_explore already returned. Only fall back to grep/glob/read for files listed under "Additional relevant files".
|
|
50
|
+
|
|
51
|
+
**The main session may only use these lightweight tools directly** (targeted lookups before edits):
|
|
52
|
+
- \`codegraph_search\` — find symbols by name
|
|
53
|
+
- \`codegraph_callers\` / \`codegraph_callees\` — trace call flow
|
|
54
|
+
- \`codegraph_impact\` — check what's affected before editing
|
|
55
|
+
- \`codegraph_node\` — get a single symbol's details`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
function replaceBetween(content, start, end, replacement) {
|
|
60
|
+
if (!content.includes(start) || !content.includes(end)) return content
|
|
61
|
+
const pattern = new RegExp(`${start}[\\s\\S]*?${end}`)
|
|
62
|
+
return content.replace(pattern, `${start}\n${replacement.trim()}\n${end}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function configureObGlobal(ctx = {}, tokenOpt = {}) {
|
|
66
|
+
const cwd = process.cwd()
|
|
67
|
+
const skillPath = path.join(cwd, '.agents', 'skills', 'ob-global', 'SKILL.md')
|
|
68
|
+
|
|
69
|
+
if (!await fse.pathExists(skillPath)) {
|
|
70
|
+
warn('ob-global skill not found, skipping dynamic configuration')
|
|
71
|
+
return { configured: false }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sourceRootsSection = buildSourceRootsSection(ctx.sourceRoots, cwd)
|
|
75
|
+
const rtkSection = buildRtkSection(!!tokenOpt?.rtk?.optedIn)
|
|
76
|
+
const cavemanSection = buildCavemanSection(!!tokenOpt?.caveman?.optedIn)
|
|
77
|
+
const codegraphSection = buildCodegraphSection(!!tokenOpt?.codegraph?.optedIn)
|
|
78
|
+
|
|
79
|
+
let content = await fse.readFile(skillPath, 'utf-8')
|
|
80
|
+
content = replaceBetween(content, SOURCE_START, SOURCE_END, sourceRootsSection)
|
|
81
|
+
content = replaceBetween(content, RTK_START, RTK_END, rtkSection)
|
|
82
|
+
content = replaceBetween(content, CAVEMAN_START, CAVEMAN_END, cavemanSection)
|
|
83
|
+
content = replaceBetween(content, CODEGRAPH_START, CODEGRAPH_END, codegraphSection)
|
|
84
|
+
await fse.writeFile(skillPath, `${content.replace(/\s*$/, '')}\n`, 'utf-8')
|
|
85
|
+
info('Configured ob-global from onboarding selections')
|
|
86
|
+
success('ob-global skill updated')
|
|
87
|
+
return { configured: true, path: skillPath }
|
|
88
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
|
|
6
|
+
vi.mock('../../utils/exec.js', () => ({
|
|
7
|
+
info: vi.fn(),
|
|
8
|
+
success: vi.fn(),
|
|
9
|
+
warn: vi.fn(),
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
import { configureObGlobal } from './global.js'
|
|
13
|
+
|
|
14
|
+
const SKILL_TEMPLATE = `## Token Optimization Rules
|
|
15
|
+
|
|
16
|
+
<!-- OB-SOURCE-ROOTS-START -->
|
|
17
|
+
placeholder
|
|
18
|
+
<!-- OB-SOURCE-ROOTS-END -->
|
|
19
|
+
|
|
20
|
+
<!-- OB-RTK-START -->
|
|
21
|
+
placeholder
|
|
22
|
+
<!-- OB-RTK-END -->
|
|
23
|
+
|
|
24
|
+
<!-- OB-CAVEMAN-START -->
|
|
25
|
+
placeholder
|
|
26
|
+
<!-- OB-CAVEMAN-END -->
|
|
27
|
+
|
|
28
|
+
<!-- OB-CODEGRAPH-START -->
|
|
29
|
+
placeholder
|
|
30
|
+
<!-- OB-CODEGRAPH-END -->
|
|
31
|
+
`
|
|
32
|
+
|
|
33
|
+
describe('configureObGlobal()', () => {
|
|
34
|
+
let tmpDir
|
|
35
|
+
let skillPath
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ob-global-test-'))
|
|
39
|
+
const skillDir = path.join(tmpDir, '.agents', 'skills', 'ob-global')
|
|
40
|
+
fs.mkdirSync(skillDir, { recursive: true })
|
|
41
|
+
skillPath = path.join(skillDir, 'SKILL.md')
|
|
42
|
+
fs.writeFileSync(skillPath, SKILL_TEMPLATE, 'utf-8')
|
|
43
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tmpDir)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
48
|
+
vi.restoreAllMocks()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('returns configured:false when ob-global skill is missing', async () => {
|
|
52
|
+
fs.rmSync(skillPath)
|
|
53
|
+
const result = await configureObGlobal()
|
|
54
|
+
expect(result).toEqual({ configured: false })
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('injects source roots section', async () => {
|
|
58
|
+
await configureObGlobal({ sourceRoots: [tmpDir] }, {})
|
|
59
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
60
|
+
expect(content).toContain('Read and analyze code ONLY from these roots')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('injects RTK section when rtk is opted in', async () => {
|
|
64
|
+
await configureObGlobal({}, { rtk: { optedIn: true } })
|
|
65
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
66
|
+
expect(content).toContain('RTK, MANDATORY')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('injects RTK not-selected note when rtk is not opted in', async () => {
|
|
70
|
+
await configureObGlobal({}, { rtk: { optedIn: false } })
|
|
71
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
72
|
+
expect(content).toContain('RTK was not selected during onboarding')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('injects caveman section when caveman is opted in', async () => {
|
|
76
|
+
await configureObGlobal({}, { caveman: { optedIn: true } })
|
|
77
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
78
|
+
expect(content).toContain('caveman mode')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('injects codegraph section when codegraph is opted in', async () => {
|
|
82
|
+
await configureObGlobal({}, { codegraph: { optedIn: true } })
|
|
83
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
84
|
+
expect(content).toContain('CodeGraph')
|
|
85
|
+
expect(content).toContain('codegraph_explore')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('injects codegraph not-selected note when codegraph is not opted in', async () => {
|
|
89
|
+
await configureObGlobal({}, { codegraph: { optedIn: false } })
|
|
90
|
+
const content = fs.readFileSync(skillPath, 'utf-8')
|
|
91
|
+
expect(content).toContain('Codegraph was not selected during onboarding')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('returns configured:true with skill path on success', async () => {
|
|
95
|
+
const result = await configureObGlobal({}, {})
|
|
96
|
+
expect(result.configured).toBe(true)
|
|
97
|
+
expect(result.path).toBe(skillPath)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -1,101 +1,109 @@
|
|
|
1
|
-
import { checkbox, confirm } from '@inquirer/prompts'
|
|
2
|
-
import fse from 'fs-extra'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import { fileURLToPath } from 'url'
|
|
5
|
-
import { code, commandExists, header, info, loading, success, warn } from '../../utils/exec.js'
|
|
6
|
-
import { installQuota } from './quota.js'
|
|
7
|
-
import { installCaveman } from './caveman.js'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
info('
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
: {
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
1
|
+
import { checkbox, confirm } from '@inquirer/prompts'
|
|
2
|
+
import fse from 'fs-extra'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import { code, commandExists, header, info, loading, success, warn } from '../../utils/exec.js'
|
|
6
|
+
import { installQuota } from './quota.js'
|
|
7
|
+
import { installCaveman } from './caveman.js'
|
|
8
|
+
import { installCodegraph } from './codegraph.js'
|
|
9
|
+
import { enableCavemanGuidance } from './caveman-guidance.js'
|
|
10
|
+
import { configureObGlobal } from './global.js'
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
13
|
+
const OPTIMIZATION_PRESET_PATH = path.resolve(__dirname, '../../presets/optimization.json')
|
|
14
|
+
const optimizationPreset = await fse.readJson(OPTIMIZATION_PRESET_PATH)
|
|
15
|
+
|
|
16
|
+
export async function checkRtk(options = {}) {
|
|
17
|
+
if (!options.skipHeader) header('Checking rtk')
|
|
18
|
+
|
|
19
|
+
let shouldCheck = true
|
|
20
|
+
if (!options.skipPrompt) {
|
|
21
|
+
info('Recommended: install and verify rtk for safer agent CLI command execution.')
|
|
22
|
+
shouldCheck = await confirm({
|
|
23
|
+
message: 'Check rtk now?',
|
|
24
|
+
default: true,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!shouldCheck) {
|
|
29
|
+
warn('Skipped rtk check (you can install it later)')
|
|
30
|
+
return { optedIn: false, checked: false, available: false }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
loading('checking rtk...')
|
|
34
|
+
|
|
35
|
+
const available = await commandExists('rtk')
|
|
36
|
+
|
|
37
|
+
if (available) {
|
|
38
|
+
success('rtk is available')
|
|
39
|
+
return { optedIn: true, checked: true, available: true }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
warn('rtk not found on PATH.')
|
|
43
|
+
console.log()
|
|
44
|
+
info('rtk is required for agents to run CLI commands safely.')
|
|
45
|
+
info('Install it from: https://github.com/rtk-ai/rtk#pre-built-binaries')
|
|
46
|
+
console.log()
|
|
47
|
+
info('After installing, verify with:')
|
|
48
|
+
code(['rtk --version'])
|
|
49
|
+
return { optedIn: true, checked: true, available: false }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function tokenOptimizationStep(options = {}) {
|
|
53
|
+
header('Step 8, Token optimization tools')
|
|
54
|
+
|
|
55
|
+
const defaultSelected = optimizationPreset.choices
|
|
56
|
+
.filter(choice => choice.checked)
|
|
57
|
+
.map(choice => choice.value)
|
|
58
|
+
let selected = defaultSelected
|
|
59
|
+
|
|
60
|
+
if (!options.skipPrompt && process.stdin.isTTY) {
|
|
61
|
+
info(optimizationPreset.info)
|
|
62
|
+
const timeoutMs = optimizationPreset.timeoutMs
|
|
63
|
+
const choice = await Promise.race([
|
|
64
|
+
checkbox({
|
|
65
|
+
message: optimizationPreset.message,
|
|
66
|
+
choices: optimizationPreset.choices,
|
|
67
|
+
}),
|
|
68
|
+
new Promise(resolve => { setTimeout(() => resolve(defaultSelected), timeoutMs) }),
|
|
69
|
+
])
|
|
70
|
+
selected = Array.isArray(choice) ? choice : defaultSelected
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
loading('applying token optimization selections...')
|
|
74
|
+
|
|
75
|
+
const installScope = options.ctx?.installScope || 'local'
|
|
76
|
+
|
|
77
|
+
const has = value => selected.includes(value)
|
|
78
|
+
|
|
79
|
+
const rtk = has('rtk')
|
|
80
|
+
? await checkRtk({ skipHeader: true, skipPrompt: true })
|
|
81
|
+
: { optedIn: false, checked: false, available: false }
|
|
82
|
+
|
|
83
|
+
const quota = has('quota')
|
|
84
|
+
? await installQuota({ skipHeader: true, skipPrompt: true })
|
|
85
|
+
: { optedIn: false, installed: false }
|
|
86
|
+
|
|
87
|
+
const caveman = has('caveman')
|
|
88
|
+
? await installCaveman({
|
|
89
|
+
skipHeader: true,
|
|
90
|
+
skipPrompt: true,
|
|
91
|
+
installScope,
|
|
92
|
+
})
|
|
93
|
+
: { optedIn: false, installed: false }
|
|
94
|
+
|
|
95
|
+
const cavemanGuidance = has('caveman')
|
|
96
|
+
? await enableCavemanGuidance(caveman)
|
|
97
|
+
: { enabled: false }
|
|
98
|
+
|
|
99
|
+
const codegraph = has('codegraph')
|
|
100
|
+
? await installCodegraph({ skipHeader: true, installScope })
|
|
101
|
+
: { optedIn: false, installed: false }
|
|
102
|
+
|
|
103
|
+
const obGlobal = await configureObGlobal(options.ctx || {}, { rtk, quota, caveman, cavemanGuidance, codegraph })
|
|
104
|
+
|
|
105
|
+
if (selected.length === 0) warn('No token optimization tools selected')
|
|
106
|
+
else success('Token optimization step completed')
|
|
107
|
+
|
|
108
|
+
return { rtk, quota, caveman, cavemanGuidance, codegraph, obGlobal }
|
|
109
|
+
}
|