@opensassi/opencode 0.1.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/AGENTS.md +35 -0
- package/README.md +81 -0
- package/bin/opencode.js +3 -0
- package/lib/cli.js +38 -0
- package/lib/commands/init.js +117 -0
- package/lib/commands/print-agents.js +6 -0
- package/lib/commands/print-skill.js +8 -0
- package/lib/commands/run.js +57 -0
- package/lib/index.js +4 -0
- package/lib/util/paths.js +21 -0
- package/package.json +40 -0
- package/scripts/asm-optimizer/run-baseline.sh +158 -0
- package/scripts/check-artifacts.js +131 -0
- package/scripts/extract-artifacts.js +204 -0
- package/scripts/install/linux/ubuntu-noble-24.04/install.sh +94 -0
- package/scripts/install/osx/macos-sequoia-15.0/install.sh +115 -0
- package/scripts/install/windows/wsl2/install.ps1 +98 -0
- package/scripts/install.ps1 +32 -0
- package/scripts/install.sh +83 -0
- package/scripts/puppeteer-config.json +3 -0
- package/scripts/test-artifacts.js +346 -0
- package/scripts/validate-all.js +18 -0
- package/scripts/verify-artifact.js +157 -0
- package/skills/asm-optimizer/SKILL.md +295 -0
- package/skills/daily-evaluation/SKILL.md +86 -0
- package/skills/git/SKILL.md +100 -0
- package/skills/issue/SKILL.md +104 -0
- package/skills/npm-optimizer/SKILL.md +218 -0
- package/skills/opensassi/SKILL.md +77 -0
- package/skills/opensassi/scripts/ensure-gitignore.sh +89 -0
- package/skills/opensassi/scripts/env-check.ps1 +139 -0
- package/skills/opensassi/scripts/env-check.sh +200 -0
- package/skills/opensassi/scripts/install-flamegraph.sh +32 -0
- package/skills/opensassi/scripts/install-npm-deps.sh +25 -0
- package/skills/profiler/SKILL.md +213 -0
- package/skills/profiler/scripts/benchmark.sh +63 -0
- package/skills/profiler/scripts/common.sh +55 -0
- package/skills/profiler/scripts/compare.sh +63 -0
- package/skills/profiler/scripts/profile.sh +63 -0
- package/skills/profiler/scripts/setup.sh +32 -0
- package/skills/session-evaluation/SKILL.md +128 -0
- package/skills/skill-manager/SKILL.md +251 -0
- package/skills/system-design/SKILL.md +558 -0
- package/skills/system-design-review/SKILL.md +396 -0
- package/skills/todo/SKILL.md +165 -0
- package/skills-index.json +137 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# opencode Agent Instructions — @opensassi/opencode
|
|
2
|
+
|
|
3
|
+
This project uses the **@opensassi/opencode** skill pack.
|
|
4
|
+
All skills, scripts, and tooling are delivered via the npm package.
|
|
5
|
+
|
|
6
|
+
## Available Skills
|
|
7
|
+
|
|
8
|
+
| `skill` | Use case |
|
|
9
|
+
|---------|----------|
|
|
10
|
+
| `asm-optimizer` | SIMD/assembly optimization framework |
|
|
11
|
+
| `daily-evaluation` | Aggregate session evaluations into dashboards |
|
|
12
|
+
| `git` | Rebase-based single-commit-per-session workflow |
|
|
13
|
+
| `issue` | GitHub issue management |
|
|
14
|
+
| `npm-optimizer` | Port an npm package to a C++ native addon |
|
|
15
|
+
| `opensassi` | Bootstrap a new project environment |
|
|
16
|
+
| `profiler` | Linux perf profiling + flamegraphs |
|
|
17
|
+
| `session-evaluation` | Generate structured session reports |
|
|
18
|
+
| `skill-manager` | Create/revise skills interactively |
|
|
19
|
+
| `system-design` | Interactive C++ spec authoring with diagrams |
|
|
20
|
+
| `system-design-review` | Seven-expert panel audit of technical specs |
|
|
21
|
+
| `todo` | Create issues + debugging skills from session context |
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
1. `skill opensassi` — Load the bootstrap skill. It exposes the full skills-index as a reference table.
|
|
26
|
+
2. Run `npx @opensassi/opencode <skill-name>` to load any sub-skill. The agent reads the output as the skill's full instructions.
|
|
27
|
+
3. Use the skill's commands. Scripts are run via `npx @opensassi/opencode run <path>` or `npx @opensassi/opencode run --skill <name> <path>`.
|
|
28
|
+
|
|
29
|
+
## Design Constraints
|
|
30
|
+
|
|
31
|
+
- No commits during development — all changes staged at finish session time
|
|
32
|
+
- Single atomic commit per session
|
|
33
|
+
- Full test suite after every rebase
|
|
34
|
+
- Session evaluation is read-only (generate) / write-once (export)
|
|
35
|
+
- All skills, scripts, and AGENTS.md live in the npm package, not in the project
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @opensassi/opencode
|
|
2
|
+
|
|
3
|
+
Agent skill harness for AI-assisted software development. Delivers 12 domain-specific skills (system-design, git workflow, profiling, etc.) and supporting scripts as a standalone npm CLI package.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx @opensassi/opencode init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install --dev @opensassi/opencode
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then bootstrap a project to make its skills available to opencode:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx opencode init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This writes three files to your project:
|
|
22
|
+
- **`AGENTS.md`** — appends full opensassi agent instructions
|
|
23
|
+
- **`.opencode/skills/opensassi/SKILL.md`** — bootstrap skill (the only skill on disk)
|
|
24
|
+
- **`.opencode/opencode.json`** — permission rules + MCP server config
|
|
25
|
+
|
|
26
|
+
All other skills (system-design, git, profiler, etc.) are loaded at runtime via the CLI.
|
|
27
|
+
|
|
28
|
+
## CLI Commands
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
npx @opensassi/opencode init # Bootstrap project
|
|
32
|
+
npx @opensassi/opencode <skill-name> # Print skill instructions to stdout
|
|
33
|
+
npx @opensassi/opencode run <path> [args...] # Run a script from the package
|
|
34
|
+
npx @opensassi/opencode run --skill <n> <path> # Run a skill-specific script
|
|
35
|
+
npx @opensassi/opencode help # Show help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Skills
|
|
39
|
+
|
|
40
|
+
| Skill | Purpose |
|
|
41
|
+
|-------|---------|
|
|
42
|
+
| `asm-optimizer` | SIMD/assembly optimization framework |
|
|
43
|
+
| `daily-evaluation` | Aggregate session evaluations into dashboards |
|
|
44
|
+
| `git` | Rebase-based single-commit-per-session workflow |
|
|
45
|
+
| `issue` | GitHub issue management |
|
|
46
|
+
| `npm-optimizer` | Port an npm package to a C++ native addon |
|
|
47
|
+
| `opensassi` | Bootstrap a new project environment |
|
|
48
|
+
| `profiler` | Linux perf profiling + flamegraphs |
|
|
49
|
+
| `session-evaluation` | Generate structured session reports |
|
|
50
|
+
| `skill-manager` | Create/revise skills interactively |
|
|
51
|
+
| `system-design` | Interactive C++ spec authoring with diagrams |
|
|
52
|
+
| `system-design-review` | Seven-expert panel audit of technical specs |
|
|
53
|
+
| `todo` | Create issues + debugging skills from session context |
|
|
54
|
+
|
|
55
|
+
## Package Contents
|
|
56
|
+
|
|
57
|
+
| Directory | Contents |
|
|
58
|
+
|-----------|----------|
|
|
59
|
+
| `bin/` | CLI entry point (`opencode` binary) |
|
|
60
|
+
| `lib/` | Programmatic API + command implementations |
|
|
61
|
+
| `skills/` | 12 skill definitions (SKILL.md) + skill scripts |
|
|
62
|
+
| `scripts/` | Artifact pipeline (extract, test, verify, check) + installers |
|
|
63
|
+
| `AGENTS.md` | Agent harness instructions (appended by init) |
|
|
64
|
+
| `skills-index.json` | Pre-built static skill/command index |
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone git@github.com:opensassi/opencode.git
|
|
70
|
+
cd opencode
|
|
71
|
+
node bin/opencode.js help
|
|
72
|
+
npm pack --dry-run # Review package contents
|
|
73
|
+
npm run validate-all # Full artifact pipeline test
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Design
|
|
77
|
+
|
|
78
|
+
- **Minimal filesystem footprint** — `init` writes only 3 files. All sub-skills load from the npm package at runtime.
|
|
79
|
+
- **opencode-agnostic** — The CLI works stand-alone. Skills are consumed by opencode but the package doesn't depend on it.
|
|
80
|
+
- **Runtime skill loading** — Sub-skills are never on disk; the agent loads them by running `npx @opensassi/opencode <name>`.
|
|
81
|
+
- **Programmatic API** — `import { init, run, printSkill } from '@opensassi/opencode'`
|
package/bin/opencode.js
ADDED
package/lib/cli.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { initCommand } from './commands/init.js'
|
|
2
|
+
import { printSkill } from './commands/print-skill.js'
|
|
3
|
+
import { runCommand } from './commands/run.js'
|
|
4
|
+
|
|
5
|
+
export async function cli(args) {
|
|
6
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === 'help') {
|
|
7
|
+
console.log(`Usage: opencode <command> [options]
|
|
8
|
+
|
|
9
|
+
Commands:
|
|
10
|
+
init Bootstrap this directory with opensassi skills
|
|
11
|
+
run <path> [args...] Run a script from the package scripts/ directory
|
|
12
|
+
run --skill <name> <path> Run a script from a skill's scripts/ directory
|
|
13
|
+
<skill-name> Print a skill's SKILL.md content to stdout
|
|
14
|
+
<skill-name> --command="..." Execute a skill command
|
|
15
|
+
help Show this help`)
|
|
16
|
+
return 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cmd = args[0]
|
|
20
|
+
|
|
21
|
+
if (cmd === 'init') {
|
|
22
|
+
await initCommand(args.slice(1))
|
|
23
|
+
return 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (cmd === 'run') {
|
|
27
|
+
const code = await runCommand(args.slice(1))
|
|
28
|
+
return code
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const skillContent = await printSkill(cmd)
|
|
32
|
+
if (skillContent === null) {
|
|
33
|
+
console.error(`Unknown command or skill: ${cmd}`)
|
|
34
|
+
return 1
|
|
35
|
+
}
|
|
36
|
+
console.log(skillContent)
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, appendFileSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { resolveAgents, resolveSkill, PKG_ROOT } from '../util/paths.js'
|
|
4
|
+
|
|
5
|
+
function readPackageAgents() {
|
|
6
|
+
return readFileSync(resolveAgents(), 'utf-8')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function readPackageOpensassiSkill() {
|
|
10
|
+
return readFileSync(resolveSkill('opensassi'), 'utf-8')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function defaultOpencodeJson() {
|
|
14
|
+
return JSON.stringify({
|
|
15
|
+
$schema: "https://opencode.ai/config.json",
|
|
16
|
+
permission: {
|
|
17
|
+
skill: "allow",
|
|
18
|
+
read: "allow",
|
|
19
|
+
edit: "allow",
|
|
20
|
+
glob: "allow",
|
|
21
|
+
grep: "allow",
|
|
22
|
+
bash: "ask",
|
|
23
|
+
task: "allow",
|
|
24
|
+
webfetch: "allow"
|
|
25
|
+
},
|
|
26
|
+
instructions: ["AGENTS.md"],
|
|
27
|
+
mcp: {
|
|
28
|
+
playwright: {
|
|
29
|
+
type: "local",
|
|
30
|
+
command: ["npx", "@playwright/mcp@latest", "--headless"],
|
|
31
|
+
enabled: true
|
|
32
|
+
},
|
|
33
|
+
debugger: {
|
|
34
|
+
type: "local",
|
|
35
|
+
command: ["gdb-mcp-server"],
|
|
36
|
+
enabled: true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, null, 2) + '\n'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function initCommand(args) {
|
|
43
|
+
const cwd = process.cwd()
|
|
44
|
+
const dryRun = args.includes('--dry-run')
|
|
45
|
+
const force = args.includes('--force')
|
|
46
|
+
|
|
47
|
+
const hasAgents = existsSync(resolve(cwd, 'AGENTS.md'))
|
|
48
|
+
const hasOpencodeDir = existsSync(resolve(cwd, '.opencode'))
|
|
49
|
+
const hasOpencodeJson = existsSync(resolve(cwd, '.opencode/opencode.json'))
|
|
50
|
+
const hasOpensassiSkill = existsSync(resolve(cwd, '.opencode/skills/opensassi/SKILL.md'))
|
|
51
|
+
const hasGitignore = existsSync(resolve(cwd, '.gitignore'))
|
|
52
|
+
|
|
53
|
+
if (hasOpensassiSkill && !force) {
|
|
54
|
+
console.log('Already initialized. Use --force to refresh.')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const pkgAgents = readPackageAgents()
|
|
59
|
+
const pkgSkill = readPackageOpensassiSkill()
|
|
60
|
+
|
|
61
|
+
if (dryRun) {
|
|
62
|
+
console.log('Dry-run: would write the following files')
|
|
63
|
+
if (!hasAgents) console.log(' CREATE AGENTS.md (from package)')
|
|
64
|
+
else console.log(' APPEND AGENTS.md (append opensassi instructions)')
|
|
65
|
+
console.log(' WRITE .opencode/skills/opensassi/SKILL.md')
|
|
66
|
+
console.log(' WRITE .opencode/opencode.json')
|
|
67
|
+
if (!hasGitignore) console.log(' CREATE .gitignore')
|
|
68
|
+
console.log(' APPEND .gitignore (.opencode/skills/)')
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const separator = '\n---\n\n'
|
|
73
|
+
const agentHeader = 'Instructions from: @opensassi/opencode\n\n'
|
|
74
|
+
|
|
75
|
+
if (!hasAgents) {
|
|
76
|
+
writeFileSync(resolve(cwd, 'AGENTS.md'), pkgAgents, 'utf-8')
|
|
77
|
+
console.log('Created AGENTS.md')
|
|
78
|
+
} else {
|
|
79
|
+
const existing = readFileSync(resolve(cwd, 'AGENTS.md'), 'utf-8')
|
|
80
|
+
if (!existing.includes('@opensassi/opencode')) {
|
|
81
|
+
appendFileSync(resolve(cwd, 'AGENTS.md'), separator + agentHeader + pkgAgents, 'utf-8')
|
|
82
|
+
console.log('Appended opensassi instructions to AGENTS.md')
|
|
83
|
+
} else {
|
|
84
|
+
console.log('AGENTS.md already includes opensassi instructions')
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
const skillDir = resolve(cwd, '.opencode/skills/opensassi')
|
|
90
|
+
if (!existsSync(skillDir)) mkdirSync(skillDir, { recursive: true })
|
|
91
|
+
writeFileSync(resolve(skillDir, 'SKILL.md'), pkgSkill, 'utf-8')
|
|
92
|
+
console.log('Wrote .opencode/skills/opensassi/SKILL.md')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!hasOpencodeJson) {
|
|
96
|
+
const opencodeDir = resolve(cwd, '.opencode')
|
|
97
|
+
if (!existsSync(opencodeDir)) mkdirSync(opencodeDir, { recursive: true })
|
|
98
|
+
writeFileSync(resolve(opencodeDir, 'opencode.json'), defaultOpencodeJson(), 'utf-8')
|
|
99
|
+
console.log('Wrote .opencode/opencode.json')
|
|
100
|
+
} else {
|
|
101
|
+
console.log('.opencode/opencode.json already exists (skipped)')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const gitignorePath = resolve(cwd, '.gitignore')
|
|
105
|
+
if (!hasGitignore) {
|
|
106
|
+
writeFileSync(gitignorePath, '.opencode/skills/\n', 'utf-8')
|
|
107
|
+
console.log('Created .gitignore with .opencode/skills/ rule')
|
|
108
|
+
} else {
|
|
109
|
+
const gitignore = readFileSync(gitignorePath, 'utf-8')
|
|
110
|
+
if (!gitignore.includes('.opencode/skills/')) {
|
|
111
|
+
appendFileSync(gitignorePath, '\n.opencode/skills/\n', 'utf-8')
|
|
112
|
+
console.log('Added .opencode/skills/ to .gitignore')
|
|
113
|
+
} else {
|
|
114
|
+
console.log('.opencode/skills/ already in .gitignore')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { spawn } from 'node:child_process'
|
|
3
|
+
import { resolveSkillScript, resolveScript, PKG_ROOT } from '../util/paths.js'
|
|
4
|
+
|
|
5
|
+
export async function runCommand(args) {
|
|
6
|
+
const skillIdx = args.indexOf('--skill')
|
|
7
|
+
let resolvedPath
|
|
8
|
+
let restArgs
|
|
9
|
+
|
|
10
|
+
if (skillIdx !== -1) {
|
|
11
|
+
const skillName = args[skillIdx + 1]
|
|
12
|
+
if (!skillName) {
|
|
13
|
+
console.error('--skill requires a name argument')
|
|
14
|
+
return 1
|
|
15
|
+
}
|
|
16
|
+
const scriptPath = args[skillIdx + 2]
|
|
17
|
+
if (!scriptPath) {
|
|
18
|
+
console.error('--skill requires a script path after the skill name')
|
|
19
|
+
return 1
|
|
20
|
+
}
|
|
21
|
+
resolvedPath = resolveSkillScript(skillName, scriptPath)
|
|
22
|
+
restArgs = args.slice(skillIdx + 3)
|
|
23
|
+
} else {
|
|
24
|
+
const scriptPath = args[0]
|
|
25
|
+
if (!scriptPath) {
|
|
26
|
+
console.error('Usage: opencode run <script> [args...]')
|
|
27
|
+
return 1
|
|
28
|
+
}
|
|
29
|
+
resolvedPath = resolveScript(scriptPath)
|
|
30
|
+
restArgs = args.slice(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!existsSync(resolvedPath)) {
|
|
34
|
+
console.error(`Script not found: ${resolvedPath}`)
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ext = resolvedPath.split('.').pop()
|
|
39
|
+
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
let proc
|
|
42
|
+
if (ext === 'sh' || ext === 'bash') {
|
|
43
|
+
proc = spawn('bash', [resolvedPath, ...restArgs], { stdio: 'inherit' })
|
|
44
|
+
} else if (ext === 'ps1') {
|
|
45
|
+
proc = spawn('powershell', ['-File', resolvedPath, ...restArgs], { stdio: 'inherit' })
|
|
46
|
+
} else if (ext === 'js' || ext === 'mjs') {
|
|
47
|
+
proc = spawn(process.execPath, [resolvedPath, ...restArgs], { stdio: 'inherit' })
|
|
48
|
+
} else {
|
|
49
|
+
proc = spawn(resolvedPath, restArgs, { stdio: 'inherit' })
|
|
50
|
+
}
|
|
51
|
+
proc.on('exit', (code) => resolve(code ?? 1))
|
|
52
|
+
proc.on('error', (err) => {
|
|
53
|
+
console.error(`Failed to run script: ${err.message}`)
|
|
54
|
+
resolve(1)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
5
|
+
export const PKG_ROOT = resolve(__dirname, '../..')
|
|
6
|
+
|
|
7
|
+
export function resolveScript(path) {
|
|
8
|
+
return resolve(PKG_ROOT, 'scripts', path)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveSkillScript(skillName, path) {
|
|
12
|
+
return resolve(PKG_ROOT, 'skills', skillName, 'scripts', path)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolveSkill(skillName) {
|
|
16
|
+
return resolve(PKG_ROOT, 'skills', skillName, 'SKILL.md')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveAgents() {
|
|
20
|
+
return resolve(PKG_ROOT, 'AGENTS.md')
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opensassi/opencode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent skill harness for opencode — bootstrap, system-design, git workflow, profiling, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opencode": "bin/opencode.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "lib/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin/",
|
|
15
|
+
"lib/",
|
|
16
|
+
"skills/",
|
|
17
|
+
"scripts/",
|
|
18
|
+
"AGENTS.md",
|
|
19
|
+
"skills-index.json",
|
|
20
|
+
"!scripts/FlameGraph/",
|
|
21
|
+
"!**/*.spec.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"extract": "node scripts/extract-artifacts.js",
|
|
25
|
+
"extract:file": "node scripts/extract-artifacts.js --file",
|
|
26
|
+
"test-artifacts": "node scripts/test-artifacts.js",
|
|
27
|
+
"test-artifact": "node scripts/test-artifacts.js --file",
|
|
28
|
+
"validate-all": "node scripts/extract-artifacts.js --all && node scripts/test-artifacts.js",
|
|
29
|
+
"verify-animation": "node scripts/verify-artifact.js",
|
|
30
|
+
"check-artifacts": "node scripts/check-artifacts.js",
|
|
31
|
+
"start": "bin/opencode.js"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"playwright": "^1.60.0",
|
|
38
|
+
"sharp": "^0.34.5"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# run-baseline.sh
|
|
5
|
+
# Build a tagged release baseline and run the profiling matrix.
|
|
6
|
+
#
|
|
7
|
+
# USAGE: Customize the variables below for your project, then run:
|
|
8
|
+
# ./scripts/asm-optimizer/run-baseline.sh [--rebuild]
|
|
9
|
+
#
|
|
10
|
+
# --rebuild Force rebuild even if baseline binaries exist
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
14
|
+
BASELINE_DIR="$REPO_ROOT/perf/baseline"
|
|
15
|
+
|
|
16
|
+
# === CUSTOMIZE THESE FOR YOUR PROJECT ===
|
|
17
|
+
PROJECT_NAME="my-project"
|
|
18
|
+
TAG="v1.0.0"
|
|
19
|
+
BASELINE_SRC="$BASELINE_DIR/$PROJECT_NAME-$TAG"
|
|
20
|
+
BASELINE_BUILD="$BASELINE_DIR/build"
|
|
21
|
+
BASELINE_BIN="$BASELINE_SRC/build"
|
|
22
|
+
PROFILES_DIR="$BASELINE_DIR/profiles/default"
|
|
23
|
+
REPORTS_DIR="$BASELINE_DIR/reports"
|
|
24
|
+
TEST_INPUT="$REPO_ROOT/test/data/input.yuv"
|
|
25
|
+
PROGRAM_ARGS=("--input" "$TEST_INPUT" "--frames" "50")
|
|
26
|
+
# =========================================
|
|
27
|
+
|
|
28
|
+
# Output tree:
|
|
29
|
+
# perf/baseline/
|
|
30
|
+
# ├── <project>-<tag>/ ← git worktree checkout
|
|
31
|
+
# ├── build/ ← Release cmake build
|
|
32
|
+
# ├── profiles/default/
|
|
33
|
+
# │ ├── fast-5fr/
|
|
34
|
+
# │ ├── fast-50fr/
|
|
35
|
+
# │ ├── slow-5fr/
|
|
36
|
+
# │ └── slow-50fr/
|
|
37
|
+
# └── reports/
|
|
38
|
+
# └── profile-summary.json
|
|
39
|
+
|
|
40
|
+
# Perf counter groups
|
|
41
|
+
PERF_COMPREHENSIVE="-d -d -d"
|
|
42
|
+
PERF_SIMD="-e fp_arith_inst_retired.256b_packed_single,fp_arith_inst_retired.128b_packed_single,fp_arith_inst_retired.scalar_single"
|
|
43
|
+
PERF_MEM_UOP="-e mem_load_uops_retired.l1_hit,mem_load_uops_retired.l1_miss,mem_load_uops_retired.l2_hit,mem_load_uops_retired.l2_miss,mem_load_uops_retired.l3_hit,mem_load_uops_retired_misc.l3_miss"
|
|
44
|
+
|
|
45
|
+
run_perf_pass() {
|
|
46
|
+
local label="$1"
|
|
47
|
+
local config="$2"
|
|
48
|
+
local frames="$3"
|
|
49
|
+
local events="$4"
|
|
50
|
+
local suffix="$5"
|
|
51
|
+
|
|
52
|
+
local outdir="$PROFILES_DIR/$label"
|
|
53
|
+
mkdir -p "$outdir"
|
|
54
|
+
local output="$outdir/perf-${suffix}.txt"
|
|
55
|
+
|
|
56
|
+
echo " [perf:$suffix] $config ${frames}fr ..."
|
|
57
|
+
perf stat $events -o "$output" \
|
|
58
|
+
"$BASELINE_BIN/$PROJECT_NAME" \
|
|
59
|
+
"${PROGRAM_ARGS[@]}" \
|
|
60
|
+
--config "$config" \
|
|
61
|
+
--frames "$frames" \
|
|
62
|
+
2>/dev/null || true
|
|
63
|
+
|
|
64
|
+
if [ -f "$output" ]; then
|
|
65
|
+
sed -i "1i# RUN: config=$config frames=$frames date=$(date -Iseconds)" "$output"
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
run_timing_pass() {
|
|
70
|
+
local label="$1"
|
|
71
|
+
local config="$2"
|
|
72
|
+
local frames="$3"
|
|
73
|
+
|
|
74
|
+
local outdir="$PROFILES_DIR/$label"
|
|
75
|
+
mkdir -p "$outdir"
|
|
76
|
+
local output="$outdir/timing.txt"
|
|
77
|
+
|
|
78
|
+
echo " [timing] $config ${frames}fr ..."
|
|
79
|
+
for i in 1 2 3; do
|
|
80
|
+
/usr/bin/time -f "real %e user %U sys %S" -a -o "$output" \
|
|
81
|
+
"$BASELINE_BIN/$PROJECT_NAME" \
|
|
82
|
+
"${PROGRAM_ARGS[@]}" \
|
|
83
|
+
--config "$config" \
|
|
84
|
+
--frames "$frames" \
|
|
85
|
+
2>/dev/null
|
|
86
|
+
done
|
|
87
|
+
|
|
88
|
+
sed -i "1i# RUN: config=$config frames=$frames date=$(date -Iseconds)" "$output"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# ----------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
step() { echo ""; echo "==> $1"; }
|
|
94
|
+
|
|
95
|
+
step "1. Create baseline directory structure"
|
|
96
|
+
mkdir -p "$BASELINE_DIR" "$PROFILES_DIR" "$REPORTS_DIR"
|
|
97
|
+
|
|
98
|
+
step "2. Checkout $TAG via git worktree"
|
|
99
|
+
if [ ! -d "$BASELINE_SRC" ] || [ ! -f "$BASELINE_SRC/CMakeLists.txt" ]; then
|
|
100
|
+
git -C "$REPO_ROOT" worktree add -f "$BASELINE_SRC" "$TAG"
|
|
101
|
+
echo " checked out $TAG at $BASELINE_SRC"
|
|
102
|
+
else
|
|
103
|
+
echo " source already exists"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
step "3. Build Release binary"
|
|
107
|
+
if [ ! -f "$BASELINE_BIN/$PROJECT_NAME" ] || [ "${1:-}" == "--rebuild" ]; then
|
|
108
|
+
cmake -S "$BASELINE_SRC" -B "$BASELINE_BUILD" \
|
|
109
|
+
-DCMAKE_BUILD_TYPE=Release
|
|
110
|
+
cmake --build "$BASELINE_BUILD" -j "$(nproc)"
|
|
111
|
+
echo " build complete: $BASELINE_BIN/$PROJECT_NAME"
|
|
112
|
+
else
|
|
113
|
+
echo " binary already exists"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Verify binary
|
|
117
|
+
"$BASELINE_BIN/$PROJECT_NAME" --version 2>/dev/null || echo " (no --version flag)"
|
|
118
|
+
|
|
119
|
+
step "4. Run profiling matrix"
|
|
120
|
+
CONFIGS=("fast" "slow")
|
|
121
|
+
FRAME_COUNTS=(5 50)
|
|
122
|
+
|
|
123
|
+
for config in "${CONFIGS[@]}"; do
|
|
124
|
+
for frames in "${FRAME_COUNTS[@]}"; do
|
|
125
|
+
label="${config}-${frames}fr"
|
|
126
|
+
echo "--- Profiling $label ---"
|
|
127
|
+
|
|
128
|
+
run_perf_pass "$label" "$config" "$frames" "$PERF_COMPREHENSIVE" "comprehensive"
|
|
129
|
+
run_perf_pass "$label" "$config" "$frames" "$PERF_SIMD" "simd"
|
|
130
|
+
run_perf_pass "$label" "$config" "$frames" "$PERF_MEM_UOP" "mem-uop"
|
|
131
|
+
run_timing_pass "$label" "$config" "$frames"
|
|
132
|
+
done
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
step "5. Generate summary report"
|
|
136
|
+
cat > "$REPORTS_DIR/profile-summary.json" << REPORT_EOF
|
|
137
|
+
{
|
|
138
|
+
"baseline": "$TAG",
|
|
139
|
+
"date": "REPLACE_DATE",
|
|
140
|
+
"configs": ["fast", "slow"],
|
|
141
|
+
"frames": [5, 50],
|
|
142
|
+
"counter_groups": [
|
|
143
|
+
"comprehensive", "simd", "mem-uop"
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
REPORT_EOF
|
|
147
|
+
|
|
148
|
+
sed -i "s/REPLACE_DATE/$(date -Iseconds)/" "$REPORTS_DIR/profile-summary.json"
|
|
149
|
+
|
|
150
|
+
echo ""
|
|
151
|
+
echo "=========================================="
|
|
152
|
+
echo "Baseline setup complete!"
|
|
153
|
+
echo " Source: $BASELINE_SRC"
|
|
154
|
+
echo " Build: $BASELINE_BUILD"
|
|
155
|
+
echo " Binary: $BASELINE_BIN/$PROJECT_NAME"
|
|
156
|
+
echo " Profiles: $PROFILES_DIR/"
|
|
157
|
+
echo " Report: $REPORTS_DIR/profile-summary.json"
|
|
158
|
+
echo "=========================================="
|