@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainjar/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Shape how your AI thinks — composable soul, persona, and rules for AI agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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() {
@@ -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, pack: c.options.pack, template: scaffold }
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({
@@ -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() {