@brainjar/cli 0.4.0 → 0.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.
- package/package.json +1 -1
- package/src/commands/persona.ts +55 -0
- package/src/commands/rules.ts +44 -3
- package/src/commands/soul.ts +50 -0
package/package.json
CHANGED
package/src/commands/persona.ts
CHANGED
|
@@ -86,6 +86,61 @@ export const persona = Cli.create('persona', {
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
})
|
|
89
|
+
.command('update', {
|
|
90
|
+
description: 'Update a persona\'s content (reads from stdin)',
|
|
91
|
+
args: z.object({
|
|
92
|
+
name: z.string().describe('Persona name'),
|
|
93
|
+
}),
|
|
94
|
+
options: z.object({
|
|
95
|
+
rules: z.array(z.string()).optional().describe('Update bundled rules'),
|
|
96
|
+
}),
|
|
97
|
+
async run(c) {
|
|
98
|
+
const name = normalizeSlug(c.args.name, 'persona name')
|
|
99
|
+
const api = await getApi()
|
|
100
|
+
|
|
101
|
+
// Validate it exists and get current data
|
|
102
|
+
let existing: ApiPersona
|
|
103
|
+
try {
|
|
104
|
+
existing = await api.get<ApiPersona>(`/api/v1/personas/${name}`)
|
|
105
|
+
} catch (e) {
|
|
106
|
+
if (e instanceof IncurError && e.code === ErrorCode.NOT_FOUND) {
|
|
107
|
+
throw createError(ErrorCode.PERSONA_NOT_FOUND, { params: [name] })
|
|
108
|
+
}
|
|
109
|
+
throw e
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const chunks: Uint8Array[] = []
|
|
113
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
114
|
+
chunks.push(chunk)
|
|
115
|
+
}
|
|
116
|
+
const content = Buffer.concat(chunks).toString().trim()
|
|
117
|
+
|
|
118
|
+
// Validate rules if provided
|
|
119
|
+
const rulesList = c.options.rules
|
|
120
|
+
if (rulesList && rulesList.length > 0) {
|
|
121
|
+
const available = await api.get<ApiRuleList>('/api/v1/rules')
|
|
122
|
+
const availableSlugs = available.rules.map(r => r.slug)
|
|
123
|
+
const invalid = rulesList.filter(r => !availableSlugs.includes(r))
|
|
124
|
+
if (invalid.length > 0) {
|
|
125
|
+
throw createError(ErrorCode.RULES_NOT_FOUND, {
|
|
126
|
+
message: `Rules not found: ${invalid.join(', ')}`,
|
|
127
|
+
hint: `Available rules: ${availableSlugs.join(', ')}`,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
await api.put<ApiPersona>(`/api/v1/personas/${name}`, {
|
|
133
|
+
content: content || existing.content,
|
|
134
|
+
bundled_rules: rulesList ?? existing.bundled_rules,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Sync if this persona is active
|
|
138
|
+
const state = await getEffectiveState(api)
|
|
139
|
+
if (state.persona === name) await sync({ api })
|
|
140
|
+
|
|
141
|
+
return { updated: name, rules: rulesList ?? existing.bundled_rules }
|
|
142
|
+
},
|
|
143
|
+
})
|
|
89
144
|
.command('list', {
|
|
90
145
|
description: 'List available personas',
|
|
91
146
|
async run() {
|
package/src/commands/rules.ts
CHANGED
|
@@ -18,7 +18,6 @@ export const rules = Cli.create('rules', {
|
|
|
18
18
|
}),
|
|
19
19
|
options: z.object({
|
|
20
20
|
description: z.string().optional().describe('One-line description of the rule'),
|
|
21
|
-
pack: z.boolean().default(false).describe('Create as a rule pack (multiple entries)'),
|
|
22
21
|
}),
|
|
23
22
|
async run(c) {
|
|
24
23
|
const name = normalizeSlug(c.args.name, 'rule name')
|
|
@@ -48,18 +47,60 @@ export const rules = Cli.create('rules', {
|
|
|
48
47
|
})
|
|
49
48
|
|
|
50
49
|
if (c.agent || c.formatExplicit) {
|
|
51
|
-
return { created: name, name,
|
|
50
|
+
return { created: name, name, template: scaffold }
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
return {
|
|
55
54
|
created: name,
|
|
56
55
|
name,
|
|
57
|
-
pack: c.options.pack,
|
|
58
56
|
template: `\n${scaffold}`,
|
|
59
57
|
next: `Run \`brainjar rules show ${name}\` to view, then \`brainjar rules add ${name}\` to activate.`,
|
|
60
58
|
}
|
|
61
59
|
},
|
|
62
60
|
})
|
|
61
|
+
.command('update', {
|
|
62
|
+
description: 'Update a rule\'s content (reads from stdin)',
|
|
63
|
+
args: z.object({
|
|
64
|
+
name: z.string().describe('Rule name'),
|
|
65
|
+
}),
|
|
66
|
+
async run(c) {
|
|
67
|
+
const name = normalizeSlug(c.args.name, 'rule name')
|
|
68
|
+
const api = await getApi()
|
|
69
|
+
|
|
70
|
+
// Validate it exists
|
|
71
|
+
try {
|
|
72
|
+
await api.get<ApiRule>(`/api/v1/rules/${name}`)
|
|
73
|
+
} catch (e) {
|
|
74
|
+
if (e instanceof IncurError && e.code === ErrorCode.NOT_FOUND) {
|
|
75
|
+
throw createError(ErrorCode.RULE_NOT_FOUND, { params: [name] })
|
|
76
|
+
}
|
|
77
|
+
throw e
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const chunks: Uint8Array[] = []
|
|
81
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
82
|
+
chunks.push(chunk)
|
|
83
|
+
}
|
|
84
|
+
const content = Buffer.concat(chunks).toString().trim()
|
|
85
|
+
|
|
86
|
+
if (!content) {
|
|
87
|
+
throw createError(ErrorCode.MISSING_ARG, {
|
|
88
|
+
message: 'No content provided. Pipe content via stdin.',
|
|
89
|
+
hint: `echo "# ${name}\\n..." | brainjar rules update ${name}`,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await api.put<ApiRule>(`/api/v1/rules/${name}`, {
|
|
94
|
+
entries: [{ name: `${name}.md`, content }],
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Sync if this rule is active
|
|
98
|
+
const state = await getEffectiveState(api)
|
|
99
|
+
if (state.rules.includes(name)) await sync({ api })
|
|
100
|
+
|
|
101
|
+
return { updated: name }
|
|
102
|
+
},
|
|
103
|
+
})
|
|
63
104
|
.command('list', {
|
|
64
105
|
description: 'List available and active rules',
|
|
65
106
|
options: z.object({
|
package/src/commands/soul.ts
CHANGED
|
@@ -39,6 +39,15 @@ export const soul = Cli.create('soul', {
|
|
|
39
39
|
lines.push(c.options.description)
|
|
40
40
|
lines.push('')
|
|
41
41
|
}
|
|
42
|
+
lines.push('## Voice')
|
|
43
|
+
lines.push('- ')
|
|
44
|
+
lines.push('')
|
|
45
|
+
lines.push('## Character')
|
|
46
|
+
lines.push('- ')
|
|
47
|
+
lines.push('')
|
|
48
|
+
lines.push('## Standards')
|
|
49
|
+
lines.push('- ')
|
|
50
|
+
lines.push('')
|
|
42
51
|
|
|
43
52
|
const content = lines.join('\n')
|
|
44
53
|
await api.put<ApiSoul>(`/api/v1/souls/${name}`, { content })
|
|
@@ -55,6 +64,47 @@ export const soul = Cli.create('soul', {
|
|
|
55
64
|
}
|
|
56
65
|
},
|
|
57
66
|
})
|
|
67
|
+
.command('update', {
|
|
68
|
+
description: 'Update a soul\'s content (reads from stdin)',
|
|
69
|
+
args: z.object({
|
|
70
|
+
name: z.string().describe('Soul name'),
|
|
71
|
+
}),
|
|
72
|
+
async run(c) {
|
|
73
|
+
const name = normalizeSlug(c.args.name, 'soul name')
|
|
74
|
+
const api = await getApi()
|
|
75
|
+
|
|
76
|
+
// Validate it exists
|
|
77
|
+
try {
|
|
78
|
+
await api.get<ApiSoul>(`/api/v1/souls/${name}`)
|
|
79
|
+
} catch (e) {
|
|
80
|
+
if (e instanceof IncurError && e.code === ErrorCode.NOT_FOUND) {
|
|
81
|
+
throw createError(ErrorCode.SOUL_NOT_FOUND, { params: [name] })
|
|
82
|
+
}
|
|
83
|
+
throw e
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const chunks: Uint8Array[] = []
|
|
87
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
88
|
+
chunks.push(chunk)
|
|
89
|
+
}
|
|
90
|
+
const content = Buffer.concat(chunks).toString().trim()
|
|
91
|
+
|
|
92
|
+
if (!content) {
|
|
93
|
+
throw createError(ErrorCode.MISSING_ARG, {
|
|
94
|
+
message: 'No content provided. Pipe content via stdin.',
|
|
95
|
+
hint: `echo "# ${name}\\n..." | brainjar soul update ${name}`,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await api.put<ApiSoul>(`/api/v1/souls/${name}`, { content })
|
|
100
|
+
|
|
101
|
+
// Sync if this soul is active
|
|
102
|
+
const state = await getEffectiveState(api)
|
|
103
|
+
if (state.soul === name) await sync({ api })
|
|
104
|
+
|
|
105
|
+
return { updated: name }
|
|
106
|
+
},
|
|
107
|
+
})
|
|
58
108
|
.command('list', {
|
|
59
109
|
description: 'List available souls',
|
|
60
110
|
async run() {
|