clementine-agent 1.1.25 → 1.1.27

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.
@@ -812,11 +812,34 @@ export class SelfImproveLoop {
812
812
  ? metrics.advisorInsights.map(a => `- ${a}`).join('\n')
813
813
  : '(no advisor data yet)';
814
814
  const areas = this.config.areas.map(a => `'${a}'`).join(', ');
815
+ // For per-agent cycles, also pull the agent's CURRENT instructions
816
+ // (agent.md body) so the LLM proposes changes informed by what's there
817
+ // rather than blind. Without this, "improve agent X" was generating
818
+ // proposals that contradicted or duplicated standing instructions.
819
+ let agentBodyText = '';
820
+ if (this.config.agentSlug) {
821
+ try {
822
+ const agentFile = path.join(AGENTS_DIR, this.config.agentSlug, 'agent.md');
823
+ if (existsSync(agentFile)) {
824
+ const raw = readFileSync(agentFile, 'utf-8');
825
+ // Cap to keep the prompt tractable — agent.md can be 10K+ chars.
826
+ // The first 4K covers the role, personality, and most standing
827
+ // instructions; deeper sections (long examples, references) are
828
+ // less important for "what should change" decisions.
829
+ const trimmed = raw.length > 4000 ? raw.slice(0, 4000) + '\n\n[...truncated, full file at agents/' + this.config.agentSlug + '/agent.md]' : raw;
830
+ agentBodyText = `\n\n## CURRENT agent.md for "${this.config.agentSlug}"\n` +
831
+ `These are the agent's existing standing instructions — your proposals should refine or extend these, not contradict or duplicate them.\n\n` +
832
+ '```markdown\n' + trimmed + '\n```\n';
833
+ }
834
+ }
835
+ catch { /* non-fatal — fall through with empty body text */ }
836
+ }
815
837
  const agentFocusText = this.config.agentSlug
816
838
  ? `\n\n## AGENT FOCUS: ${this.config.agentSlug}\nThis is a focused improvement cycle for agent "${this.config.agentSlug}" ONLY.\n` +
817
839
  `- You MUST target area "agent" with target "${this.config.agentSlug}", OR area "cron" targeting a cron job that this agent runs.\n` +
818
840
  `- Do NOT propose changes to SOUL.md, AGENTS.md, source code, or other agents.\n` +
819
- `- Focus on improving this agent's personality, instructions, and task execution quality.\n`
841
+ `- Focus on improving this agent's personality, instructions, and task execution quality.\n` +
842
+ agentBodyText
820
843
  : '';
821
844
  // Read SOUL.md evolution candidates from FEEDBACK.md (written by synthesizeFeedbackPatterns)
822
845
  let soulCandidatesText = '';
package/dist/cli/index.js CHANGED
@@ -2130,6 +2130,189 @@ configCmd
2130
2130
  console.error(` Failed to open editor: ${editor}`);
2131
2131
  }
2132
2132
  });
2133
+ // ── Skills commands ─────────────────────────────────────────────────
2134
+ //
2135
+ // Procedural memory the agent extracts from successful runs lives at
2136
+ // vault/00-System/skills/ (global) and agents/<slug>/skills/ (per-agent).
2137
+ // New skills land in pending-approval until the owner OKs them. These
2138
+ // commands give the owner a CLI path that mirrors the dashboard UI.
2139
+ const skillsCmd = program
2140
+ .command('skills')
2141
+ .description('List, inspect, approve, and reject extracted skills');
2142
+ skillsCmd
2143
+ .command('list')
2144
+ .description('List all approved skills (global + per-agent) with use counts')
2145
+ .option('-a, --agent <slug>', 'Filter to a specific agent\'s skills')
2146
+ .option('--json', 'Emit machine-readable JSON')
2147
+ .action(async (opts) => {
2148
+ const BOLD = '\x1b[1m';
2149
+ const DIM = '\x1b[0;90m';
2150
+ const CYAN = '\x1b[0;36m';
2151
+ const RESET = '\x1b[0m';
2152
+ try {
2153
+ process.env.CLEMENTINE_HOME = BASE_DIR;
2154
+ const { listSkills } = await import('../agent/skill-extractor.js');
2155
+ const skills = listSkills(opts.agent);
2156
+ if (opts.json) {
2157
+ console.log(JSON.stringify(skills, null, 2));
2158
+ return;
2159
+ }
2160
+ if (skills.length === 0) {
2161
+ console.log();
2162
+ console.log(` ${DIM}No approved skills yet${opts.agent ? ` for "${opts.agent}"` : ''}.${RESET}`);
2163
+ console.log(` Skills get auto-extracted from successful cron / unleashed runs and queued for approval.`);
2164
+ console.log(` Pending: ${BOLD}clementine skills pending${RESET}`);
2165
+ console.log();
2166
+ return;
2167
+ }
2168
+ console.log();
2169
+ console.log(` ${BOLD}${'NAME'.padEnd(36)}${'AGENT'.padEnd(20)}${'USES'.padEnd(8)}${'UPDATED'}${RESET}`);
2170
+ console.log(` ${DIM}${'─'.repeat(80)}${RESET}`);
2171
+ for (const s of skills) {
2172
+ const agent = s.agentSlug ?? 'global';
2173
+ const updated = s.updatedAt.slice(0, 10);
2174
+ console.log(` ${s.name.slice(0, 34).padEnd(36)}${CYAN}${agent.slice(0, 18).padEnd(20)}${RESET}${String(s.useCount).padEnd(8)}${DIM}${updated}${RESET}`);
2175
+ }
2176
+ console.log();
2177
+ console.log(` ${DIM}Total: ${skills.length} skill${skills.length === 1 ? '' : 's'}.${RESET}`);
2178
+ console.log();
2179
+ }
2180
+ catch (err) {
2181
+ console.error(` Error listing skills: ${err}`);
2182
+ process.exit(1);
2183
+ }
2184
+ });
2185
+ skillsCmd
2186
+ .command('pending')
2187
+ .description('Show skills awaiting your approval')
2188
+ .option('--json', 'Emit machine-readable JSON')
2189
+ .action(async (opts) => {
2190
+ const BOLD = '\x1b[1m';
2191
+ const DIM = '\x1b[0;90m';
2192
+ const YELLOW = '\x1b[1;33m';
2193
+ const RESET = '\x1b[0m';
2194
+ try {
2195
+ process.env.CLEMENTINE_HOME = BASE_DIR;
2196
+ const { listPendingSkills } = await import('../agent/skill-extractor.js');
2197
+ const pending = listPendingSkills();
2198
+ if (opts.json) {
2199
+ console.log(JSON.stringify(pending, null, 2));
2200
+ return;
2201
+ }
2202
+ if (pending.length === 0) {
2203
+ console.log();
2204
+ console.log(` ${DIM}No skills pending approval.${RESET}`);
2205
+ console.log();
2206
+ return;
2207
+ }
2208
+ console.log();
2209
+ console.log(` ${YELLOW}${pending.length} skill${pending.length === 1 ? '' : 's'} pending approval${RESET}`);
2210
+ console.log();
2211
+ for (const s of pending) {
2212
+ const agent = s.agentSlug ? ` [agent: ${s.agentSlug}]` : '';
2213
+ console.log(` ${BOLD}${s.name}${RESET}${DIM}${agent}${RESET}`);
2214
+ console.log(` ${s.title}`);
2215
+ console.log(` ${DIM}${s.description}${RESET}`);
2216
+ console.log(` ${DIM}From ${s.source} • ${s.createdAt.slice(0, 19).replace('T', ' ')}${RESET}`);
2217
+ console.log();
2218
+ }
2219
+ console.log(` Approve: ${BOLD}clementine skills approve <name>${RESET}`);
2220
+ console.log(` Reject: ${BOLD}clementine skills reject <name>${RESET}`);
2221
+ console.log();
2222
+ }
2223
+ catch (err) {
2224
+ console.error(` Error listing pending skills: ${err}`);
2225
+ process.exit(1);
2226
+ }
2227
+ });
2228
+ skillsCmd
2229
+ .command('approve <name>')
2230
+ .description('Approve a pending skill (moves it from pending into the active library)')
2231
+ .action(async (name) => {
2232
+ const GREEN = '\x1b[0;32m';
2233
+ const RED = '\x1b[0;31m';
2234
+ const RESET = '\x1b[0m';
2235
+ try {
2236
+ process.env.CLEMENTINE_HOME = BASE_DIR;
2237
+ const { approvePendingSkill } = await import('../agent/skill-extractor.js');
2238
+ const result = approvePendingSkill(name);
2239
+ if (result.ok) {
2240
+ console.log(` ${GREEN}✓${RESET} ${result.message}`);
2241
+ }
2242
+ else {
2243
+ console.error(` ${RED}✗${RESET} ${result.message}`);
2244
+ process.exit(1);
2245
+ }
2246
+ }
2247
+ catch (err) {
2248
+ console.error(` Error approving skill: ${err}`);
2249
+ process.exit(1);
2250
+ }
2251
+ });
2252
+ skillsCmd
2253
+ .command('reject <name>')
2254
+ .description('Reject a pending skill (deletes it from the queue)')
2255
+ .action(async (name) => {
2256
+ const GREEN = '\x1b[0;32m';
2257
+ const RED = '\x1b[0;31m';
2258
+ const RESET = '\x1b[0m';
2259
+ try {
2260
+ process.env.CLEMENTINE_HOME = BASE_DIR;
2261
+ const { rejectPendingSkill } = await import('../agent/skill-extractor.js');
2262
+ const result = rejectPendingSkill(name);
2263
+ if (result.ok) {
2264
+ console.log(` ${GREEN}✓${RESET} ${result.message}`);
2265
+ }
2266
+ else {
2267
+ console.error(` ${RED}✗${RESET} ${result.message}`);
2268
+ process.exit(1);
2269
+ }
2270
+ }
2271
+ catch (err) {
2272
+ console.error(` Error rejecting skill: ${err}`);
2273
+ process.exit(1);
2274
+ }
2275
+ });
2276
+ skillsCmd
2277
+ .command('search <query>')
2278
+ .description('Preview which skills would be injected for a given query — useful for debugging skill matching')
2279
+ .option('-a, --agent <slug>', 'Search as a specific agent (skills get the agent boost)')
2280
+ .option('-n, --limit <n>', 'Max matches to show', '5')
2281
+ .action(async (query, opts) => {
2282
+ const BOLD = '\x1b[1m';
2283
+ const DIM = '\x1b[0;90m';
2284
+ const CYAN = '\x1b[0;36m';
2285
+ const GREEN = '\x1b[0;32m';
2286
+ const RESET = '\x1b[0m';
2287
+ try {
2288
+ process.env.CLEMENTINE_HOME = BASE_DIR;
2289
+ const { searchSkills } = await import('../agent/skill-extractor.js');
2290
+ const limit = parseInt(opts.limit ?? '5', 10);
2291
+ const matches = searchSkills(query, limit, opts.agent);
2292
+ if (matches.length === 0) {
2293
+ console.log();
2294
+ console.log(` ${DIM}No skills matched "${query}"${opts.agent ? ` for agent ${opts.agent}` : ''}.${RESET}`);
2295
+ console.log();
2296
+ return;
2297
+ }
2298
+ console.log();
2299
+ console.log(` ${BOLD}${matches.length} skill${matches.length === 1 ? '' : 's'} matched${RESET} ${DIM}(threshold for injection: score >= 4)${RESET}`);
2300
+ console.log();
2301
+ for (const m of matches) {
2302
+ const inject = m.score >= 4 ? `${GREEN}✓ would inject${RESET}` : `${DIM}below threshold${RESET}`;
2303
+ console.log(` ${BOLD}${m.name}${RESET} ${CYAN}score: ${m.score.toFixed(2)}${RESET} ${inject}`);
2304
+ console.log(` ${m.title}`);
2305
+ if (m.toolsUsed.length > 0) {
2306
+ console.log(` ${DIM}Tools: ${m.toolsUsed.join(', ')}${RESET}`);
2307
+ }
2308
+ console.log();
2309
+ }
2310
+ }
2311
+ catch (err) {
2312
+ console.error(` Error searching skills: ${err}`);
2313
+ process.exit(1);
2314
+ }
2315
+ });
2133
2316
  // ── Brain commands ──────────────────────────────────────────────────
2134
2317
  const brainCmd = program
2135
2318
  .command('brain')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",