@soederpop/luca 0.0.10 → 0.0.12
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/commands/release.ts +29 -21
- package/package.json +2 -2
- package/src/agi/features/claude-code.ts +7 -0
- package/src/bootstrap/generated.ts +1 -1
- package/src/commands/prompt.ts +3 -0
- package/src/introspection/generated.agi.ts +984 -984
- package/src/introspection/generated.node.ts +1 -1
- package/src/introspection/generated.web.ts +1 -1
- package/src/scaffolds/generated.ts +1 -1
package/commands/release.ts
CHANGED
|
@@ -21,10 +21,10 @@ export const argsSchema = CommandOptionsSchema.extend({
|
|
|
21
21
|
async function release(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
22
22
|
const container = context.container as any
|
|
23
23
|
const proc = container.feature('proc')
|
|
24
|
-
const
|
|
24
|
+
const fileSystem = container.feature('fs')
|
|
25
25
|
const ui = container.feature('ui')
|
|
26
26
|
|
|
27
|
-
const pkg = JSON.parse(await
|
|
27
|
+
const pkg = JSON.parse(await fileSystem.readFileAsync('package.json'))
|
|
28
28
|
const version = pkg.version
|
|
29
29
|
const tag = `v${version}`
|
|
30
30
|
const distDir = 'dist/release'
|
|
@@ -45,7 +45,7 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
45
45
|
// 1. Run tests
|
|
46
46
|
if (!options.skipTests) {
|
|
47
47
|
console.log('\n→ Running tests...')
|
|
48
|
-
const testResult = await proc.
|
|
48
|
+
const testResult = await proc.execAndCapture('bun test test/*.test.ts', { silent: false })
|
|
49
49
|
if (testResult.exitCode !== 0) {
|
|
50
50
|
console.error('Tests failed. Fix them before releasing.')
|
|
51
51
|
return
|
|
@@ -62,7 +62,7 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
62
62
|
]
|
|
63
63
|
for (const [label, cmd] of steps) {
|
|
64
64
|
console.log(` ${label}...`)
|
|
65
|
-
const r = await proc.
|
|
65
|
+
const r = await proc.execAndCapture(cmd, { silent: true })
|
|
66
66
|
if (r.exitCode !== 0) {
|
|
67
67
|
console.error(`${label} failed:\n${r.stderr}`)
|
|
68
68
|
return
|
|
@@ -71,7 +71,7 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// 3. Cross-compile for all targets
|
|
74
|
-
|
|
74
|
+
fileSystem.ensureFolder(distDir)
|
|
75
75
|
|
|
76
76
|
console.log(`\n→ Compiling for ${selectedTargets.length} targets...`)
|
|
77
77
|
for (const target of selectedTargets) {
|
|
@@ -80,14 +80,14 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
80
80
|
const cmd = `bun build ./src/cli/cli.ts --compile --target=${target.bunTarget} --outfile ${outfile} --external node-llama-cpp`
|
|
81
81
|
|
|
82
82
|
console.log(` ${target.name}...`)
|
|
83
|
-
const result = await proc.
|
|
83
|
+
const result = await proc.execAndCapture(cmd, { silent: true })
|
|
84
84
|
if (result.exitCode !== 0) {
|
|
85
85
|
console.error(` Failed to compile for ${target.name}:\n${result.stderr}`)
|
|
86
86
|
return
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const
|
|
90
|
-
const sizeMB = (
|
|
89
|
+
const sizeBytes = proc.exec(`stat -f%z ${container.paths.resolve(outfile)}`)
|
|
90
|
+
const sizeMB = (parseInt(sizeBytes, 10) / 1024 / 1024).toFixed(1)
|
|
91
91
|
console.log(` ✓ ${outfile} (${sizeMB} MB)`)
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -98,14 +98,14 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// 4. Check if tag already exists
|
|
101
|
-
const tagCheck = await proc.
|
|
101
|
+
const tagCheck = await proc.execAndCapture(`git tag -l "${tag}"`, { silent: true })
|
|
102
102
|
if (tagCheck.stdout.trim() === tag) {
|
|
103
103
|
console.error(`\nTag ${tag} already exists. Bump the version in package.json first.`)
|
|
104
104
|
return
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// 5. Check for clean working tree (allow untracked)
|
|
108
|
-
const statusCheck = await proc.
|
|
108
|
+
const statusCheck = await proc.execAndCapture('git status --porcelain', { silent: true })
|
|
109
109
|
const dirtyFiles = statusCheck.stdout.trim().split('\n').filter((l: string) => l && !l.startsWith('??'))
|
|
110
110
|
if (dirtyFiles.length > 0) {
|
|
111
111
|
console.error('\nWorking tree has uncommitted changes. Commit or stash them first.')
|
|
@@ -115,7 +115,7 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
115
115
|
|
|
116
116
|
// 6. Create git tag
|
|
117
117
|
console.log(`\n→ Creating tag ${tag}...`)
|
|
118
|
-
const tagResult = await proc.
|
|
118
|
+
const tagResult = await proc.execAndCapture(`git tag -a "${tag}" -m "Release ${tag}"`, { silent: true })
|
|
119
119
|
if (tagResult.exitCode !== 0) {
|
|
120
120
|
console.error(`Failed to create tag:\n${tagResult.stderr}`)
|
|
121
121
|
return
|
|
@@ -123,29 +123,37 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
123
123
|
|
|
124
124
|
// 7. Push tag
|
|
125
125
|
console.log(`→ Pushing tag ${tag}...`)
|
|
126
|
-
const pushResult = await proc.
|
|
126
|
+
const pushResult = await proc.execAndCapture(`git push origin "${tag}"`, { silent: true })
|
|
127
127
|
if (pushResult.exitCode !== 0) {
|
|
128
128
|
console.error(`Failed to push tag:\n${pushResult.stderr}`)
|
|
129
129
|
return
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// 8. Create GitHub release and upload binaries
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
.map(t => `${distDir}/luca-${t.suffix}${t.ext || ''}`)
|
|
136
|
-
.join(' ')
|
|
133
|
+
const assetPaths = selectedTargets
|
|
134
|
+
.map(t => container.paths.resolve(`${distDir}/luca-${t.suffix}${t.ext || ''}`))
|
|
137
135
|
|
|
138
136
|
const releaseTitle = `Luca ${tag}`
|
|
139
137
|
const releaseNotes = await generateReleaseNotes(proc, tag)
|
|
140
138
|
|
|
139
|
+
// Write notes to a temp file so gh doesn't need shell quoting
|
|
140
|
+
const notesFile = container.paths.resolve(distDir, 'release-notes.md')
|
|
141
|
+
await fileSystem.writeFileAsync(notesFile, releaseNotes)
|
|
142
|
+
|
|
141
143
|
console.log(`\n→ Creating GitHub release ${tag}...`)
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
+
const ghArgs = [
|
|
145
|
+
'release', 'create', tag,
|
|
146
|
+
...assetPaths,
|
|
147
|
+
'--title', releaseTitle,
|
|
148
|
+
'--notes-file', notesFile,
|
|
149
|
+
...(options.draft ? ['--draft'] : []),
|
|
150
|
+
]
|
|
151
|
+
const ghResult = await proc.spawnAndCapture('gh', ghArgs)
|
|
144
152
|
|
|
145
153
|
if (ghResult.exitCode !== 0) {
|
|
146
154
|
console.error(`Failed to create GitHub release:\n${ghResult.stderr}`)
|
|
147
155
|
console.log('The tag was pushed. You can manually create the release with:')
|
|
148
|
-
console.log(` gh release create ${tag} ${
|
|
156
|
+
console.log(` gh release create ${tag} ${assetPaths.join(' ')}`)
|
|
149
157
|
return
|
|
150
158
|
}
|
|
151
159
|
|
|
@@ -155,7 +163,7 @@ async function release(options: z.infer<typeof argsSchema>, context: ContainerCo
|
|
|
155
163
|
|
|
156
164
|
async function generateReleaseNotes(proc: any, tag: string): Promise<string> {
|
|
157
165
|
// Get commits since last tag
|
|
158
|
-
const lastTag = await proc.
|
|
166
|
+
const lastTag = await proc.execAndCapture('git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo ""', { silent: true })
|
|
159
167
|
const since = lastTag.stdout.trim()
|
|
160
168
|
|
|
161
169
|
let logCmd: string
|
|
@@ -165,7 +173,7 @@ async function generateReleaseNotes(proc: any, tag: string): Promise<string> {
|
|
|
165
173
|
logCmd = 'git log --oneline --no-decorate -20'
|
|
166
174
|
}
|
|
167
175
|
|
|
168
|
-
const log = await proc.
|
|
176
|
+
const log = await proc.execAndCapture(logCmd, { silent: true })
|
|
169
177
|
const commits = log.stdout.trim()
|
|
170
178
|
|
|
171
179
|
return `## What's Changed\n\n${commits ? commits.split('\n').map((c: string) => `- ${c}`).join('\n') : 'Initial release'}\n\n## Platforms\n\n- Linux x64\n- Linux ARM64\n- macOS x64 (Intel)\n- macOS ARM64 (Apple Silicon)\n- Windows x64`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soederpop/luca",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"website": "https://luca.soederpop.com",
|
|
5
5
|
"description": "lightweight universal conversational architecture AKA Le Ultimate Component Architecture AKA Last Universal Common Ancestor, part AI part Human",
|
|
6
6
|
"author": "jon soeder aka the people's champ <jon@soederpop.com>",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"chokidar": "^3.5.3",
|
|
103
103
|
"cli-markdown": "^3.5.0",
|
|
104
104
|
"compromise": "^14.14.5",
|
|
105
|
-
"contentbase": "^0.1.
|
|
105
|
+
"contentbase": "^0.1.4",
|
|
106
106
|
"cors": "^2.8.5",
|
|
107
107
|
"detect-port": "^1.5.1",
|
|
108
108
|
"dotenv": "^17.2.4",
|
|
@@ -182,6 +182,8 @@ export const ClaudeCodeOptionsSchema = FeatureOptionsSchema.extend({
|
|
|
182
182
|
settingsFile: z.string().optional().describe('Path to a custom settings file'),
|
|
183
183
|
/** Directories containing Claude Code skills (SKILL.md files) to load into sessions. Passed as --add-dir. */
|
|
184
184
|
skillsFolders: z.array(z.string()).optional().describe('Directories containing Claude Code skills to load into sessions'),
|
|
185
|
+
/** Launch Claude Code with a Chrome browser tool. */
|
|
186
|
+
chrome: z.boolean().optional().describe('Launch Claude Code with a Chrome browser tool'),
|
|
185
187
|
})
|
|
186
188
|
|
|
187
189
|
export const ClaudeCodeEventsSchema = FeatureEventsSchema.extend({
|
|
@@ -265,6 +267,8 @@ export interface RunOptions {
|
|
|
265
267
|
debugFile?: string
|
|
266
268
|
/** Path to a custom settings file. */
|
|
267
269
|
settingsFile?: string
|
|
270
|
+
/** Launch Claude Code with a Chrome browser tool. */
|
|
271
|
+
chrome?: boolean
|
|
268
272
|
}
|
|
269
273
|
|
|
270
274
|
/**
|
|
@@ -597,6 +601,9 @@ export class ClaudeCode extends Feature<ClaudeCodeState, ClaudeCodeOptions> {
|
|
|
597
601
|
|
|
598
602
|
if (options.debugFile) args.push('--debug-file', options.debugFile)
|
|
599
603
|
|
|
604
|
+
const chrome = options.chrome ?? this.options.chrome
|
|
605
|
+
if (chrome) args.push('--chrome')
|
|
606
|
+
|
|
600
607
|
if (options.extraArgs?.length) {
|
|
601
608
|
args.push(...options.extraArgs)
|
|
602
609
|
}
|
package/src/commands/prompt.ts
CHANGED
|
@@ -21,6 +21,7 @@ export const argsSchema = CommandOptionsSchema.extend({
|
|
|
21
21
|
'repeat-anyway': z.boolean().default(false).describe('Run even if repeatable is false and the prompt has already been run'),
|
|
22
22
|
'parallel': z.boolean().default(false).describe('Run multiple prompt files in parallel with side-by-side terminal UI'),
|
|
23
23
|
'exclude-sections': z.string().optional().describe('Comma-separated list of section headings to exclude from the prompt'),
|
|
24
|
+
'chrome': z.boolean().default(false).describe('Launch Claude Code with a Chrome browser tool'),
|
|
24
25
|
})
|
|
25
26
|
|
|
26
27
|
const CLI_TARGETS = new Set(['claude', 'codex'])
|
|
@@ -125,6 +126,7 @@ async function runClaudeOrCodex(target: 'claude' | 'codex', promptContent: strin
|
|
|
125
126
|
|
|
126
127
|
if (target === 'claude') {
|
|
127
128
|
runOptions.permissionMode = options['permission-mode']
|
|
129
|
+
if (options.chrome) runOptions.chrome = true
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
const startTime = Date.now()
|
|
@@ -273,6 +275,7 @@ async function runParallel(
|
|
|
273
275
|
|
|
274
276
|
if (target === 'claude') {
|
|
275
277
|
runOptions.permissionMode = options['permission-mode']
|
|
278
|
+
if (options.chrome) runOptions.chrome = true
|
|
276
279
|
}
|
|
277
280
|
|
|
278
281
|
feature.on('session:message', ({ sessionId, message }: { sessionId: string; message: any }) => {
|