Skip to content

feat(roles): Add configurable AI agent personas (Sage, Harvester, Curator, etc.)#41

Open
jbdevprimary wants to merge 17 commits into
mainfrom
feat/sage-command
Open

feat(roles): Add configurable AI agent personas (Sage, Harvester, Curator, etc.)#41
jbdevprimary wants to merge 17 commits into
mainfrom
feat/sage-command

Conversation

@jbdevprimary
Copy link
Copy Markdown
Contributor

@jbdevprimary jbdevprimary commented Dec 28, 2025

Summary

Adds a roles module to @agentic-dev-library/control for configurable AI agent personas that repositories can use out of the box or customize.

New Files

  • src/roles/types.ts - RoleDefinition, RoleConfig, RoleTrigger, AgentCapability types
  • src/roles/defaults.ts - Default role definitions for Sage, Harvester, Curator, Reviewer, Fixer, Delegator
  • src/roles/executor.ts - Role execution with executeRole, executeSageRole
  • src/roles/index.ts - Module exports

Changes

  • src/cli.ts - Added agentic roles commands (list, info, sage, match)
  • src/core/config.ts - Added RolesConfig to AgenticConfig
  • src/index.ts - Export roles module

Default Roles

Role Icon Purpose Triggers
Sage 🔮 Q&A, task decomposition, agent routing @Sage, /sage
Harvester 🌾 PR lifecycle, review management, merging Schedule (15min)
Curator 🎭 Nightly triage, issue assessment, agent spawning Schedule (2am)
Reviewer 🔍 AI code review PR events, /review
Fixer 🔧 CI failure resolution CI failure events
Delegator 📋 Issue to agent routing /cursor, /jules

CLI Usage

```bash

List all roles

agentic roles list

Get role details

agentic roles info sage

Run Sage directly

agentic roles sage "How do I add a new endpoint?"

Find which role matches a trigger

agentic roles match "@Sage"
```

Configuration

```json
{
"roles": {
"sage": {
"enabled": true,
"model": "claude-sonnet-4-20250514"
},
"harvester": {
"enabled": false
}
}
}
```

Test Plan

  • TypeScript compiles without errors
  • CLI commands work
  • Integration tests with actual execution

Closes #40


Note

Introduces configurable AI roles with default personas and execution utilities, plus CLI and config integration.

  • New roles/ module: types.ts (role and config types), defaults.ts (built-in roles), executor.ts (executeRole, executeSageRole, listRoles, findRoleByTrigger, getEffectiveRole), and index.ts exports
  • CLI: adds agentic roles subcommands — list, info, sage, match in src/cli.ts
  • Config: extends AgenticConfig with roles and RoleConfig/RolesConfig in src/core/config.ts
  • Public API: re-exports roles module from src/index.ts

Written by Cursor Bugbot for commit 597bad6. This will update automatically on new commits. Configure here.

…ator, etc.)

- Add roles module with RoleDefinition, RoleConfig, RoleTrigger types
- Add default roles: Sage, Harvester, Curator, Reviewer, Fixer, Delegator
- Add role executor with executeRole, executeSageRole functions
- Add roles CLI commands: list, info, sage, match
- Add RolesConfig to AgenticConfig for agentic.config.json support
- Export roles module from package index

Closes #40
@jbdevprimary
Copy link
Copy Markdown
Contributor Author

/gemini review

@jbdevprimary
Copy link
Copy Markdown
Contributor Author

/q review

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @jbdevprimary, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the @agentic-dev-library/control by introducing a robust system for configurable AI agent personas. It provides a framework for defining specialized AI behaviors, complete with distinct purposes, capabilities, and activation triggers. This allows for a more structured and adaptable approach to integrating AI into development workflows, enabling repositories to leverage predefined roles or create custom ones to suit their specific needs.

Highlights

  • New Roles Module: Introduced a new roles module within @agentic-dev-library/control to define and manage configurable AI agent personas.
  • Default AI Agent Personas: Added six default AI agent roles: Sage (Q&A, task decomposition), Harvester (PR lifecycle), Curator (nightly triage), Reviewer (AI code review), Fixer (CI failure resolution), and Delegator (issue routing).
  • CLI Integration: Extended the CLI with new agentic roles commands, allowing users to list available roles, view detailed information, directly run the Sage advisor, and match roles based on trigger patterns.
  • Configurability: Enabled configuration of these AI roles via agentic.config.json, allowing repositories to customize or override default behaviors, system prompts, models, and capabilities for each role.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a powerful and well-structured roles module for defining configurable AI agent personas, which is a great addition. The implementation includes default roles, an execution engine, and corresponding CLI commands. The code is generally well-written and the feature is comprehensive.

My review focuses on a couple of key areas. There's a critical issue with the configuration type definitions in src/core/config.ts, which are incomplete and prevent users from accessing the full customization features like custom roles or trigger overrides. This also points to a structural issue with circular dependencies that should be addressed. Additionally, I've noted a type safety issue in the new CLI command for the sage role that could be improved.

Comment on lines +88 to +110
export interface RoleConfig {
/** Enable/disable the role */
enabled?: boolean;
/** Override the system prompt */
systemPrompt?: string;
/** Model override */
model?: string;
}

export interface RolesConfig {
/** Sage role configuration */
sage?: RoleConfig;
/** Harvester role configuration */
harvester?: RoleConfig;
/** Curator role configuration */
curator?: RoleConfig;
/** Reviewer role configuration */
reviewer?: RoleConfig;
/** Fixer role configuration */
fixer?: RoleConfig;
/** Delegator role configuration */
delegator?: RoleConfig;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The RoleConfig and RolesConfig interfaces are incomplete compared to their definitions in src/roles/types.ts, which prevents users from using the full range of role customization features.

  • RoleConfig is missing triggers, additionalCapabilities, revokedCapabilities, and settings.
  • RolesConfig is missing the custom property for defining custom roles.

Adding these properties by importing types from src/roles/types.ts would create a circular dependency (core/config.ts -> roles/types.ts -> ... -> core/config.ts).

To resolve this structural issue, I recommend moving the core role type definitions (RoleTrigger, AgentCapability, RoleDefinition) from src/roles/types.ts to a more central location that doesn't create import cycles, such as src/core/types.ts. Once the types are moved, you can update RoleConfig and RolesConfig here to be complete.

Comment thread packages/agentic-control/src/cli.ts Outdated

console.log('🔮 Sage is thinking...\n');

const result = await executeSageRole(query, model as Parameters<typeof executeSageRole>[1]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type assertion model as Parameters<typeof executeSageRole>[1] is unsafe because the model variable is of type unknown. This bypasses TypeScript's type safety and could lead to runtime errors if the provider loading logic changes.

The root cause appears to be the ModelFactory type definition in src/core/providers.ts, which returns unknown. To improve type safety, consider updating ModelFactory to return LanguageModel from the ai package. This would allow the correct type to flow through the system, making this assertion unnecessary.

@coveralls
Copy link
Copy Markdown

coveralls commented Dec 28, 2025

Pull Request Test Coverage Report for Build 20566069519

Details

  • 7 of 78 (8.97%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.3%) to 13.204%

Changes Missing Coverage Covered Lines Changed/Added Lines %
packages/agentic-control/src/roles/defaults.ts 7 9 77.78%
packages/agentic-control/src/roles/executor.ts 0 69 0.0%
Totals Coverage Status
Change from base Build 20535845403: -0.3%
Covered Lines: 305
Relevant Lines: 2018

💛 - Coveralls

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a well-structured and powerful roles module for defining configurable AI agent personas. The separation of types, defaults, and execution logic is clean. My review focuses on ensuring this new system is used consistently and correctly throughout the codebase. The main points are:

  • A critical issue with redundant and incomplete type definitions in the configuration file that should be consolidated.
  • A critical bug in the new CLI commands where they fail to load and respect user-defined role configurations.
  • An inconsistency in the sage CLI command, which bypasses the new roles configuration system.
  • Some minor improvements for type safety and code clarity.

Addressing these points will make the new feature more robust and consistent.

Comment thread packages/agentic-control/src/cli.ts Outdated
Comment on lines +953 to +954
.action((opts) => {
const roles = listRoles();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The roles CLI commands (list, info, match) do not load the user's configuration from agentic.config.json. As a result, they only operate on the default, hardcoded role definitions. This means any user customizations (disabling roles, overriding prompts, defining custom roles) will be completely ignored by the CLI.

You should load the configuration at the beginning of each action and pass it to the role helper functions.
For example:

import { getConfig } from './core/config.js';
// ...
.action((opts) => {
  const config = getConfig();
  const roles = listRoles(config.roles);
  // ...
});

This needs to be applied to the list, info, and match commands.

Comment on lines +88 to +110
export interface RoleConfig {
/** Enable/disable the role */
enabled?: boolean;
/** Override the system prompt */
systemPrompt?: string;
/** Model override */
model?: string;
}

export interface RolesConfig {
/** Sage role configuration */
sage?: RoleConfig;
/** Harvester role configuration */
harvester?: RoleConfig;
/** Curator role configuration */
curator?: RoleConfig;
/** Reviewer role configuration */
reviewer?: RoleConfig;
/** Fixer role configuration */
fixer?: RoleConfig;
/** Delegator role configuration */
delegator?: RoleConfig;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There are redundant and incomplete definitions for RoleConfig and RolesConfig here. The complete and correct types are already defined in packages/agentic-control/src/roles/types.ts.

Using these incomplete local definitions will cause issues. For example, properties like custom roles or additionalCapabilities in the configuration will be ignored or cause type errors.

You should remove these interfaces and instead import them from ../roles/types.js where they are used (e.g., in AgenticConfig).

.option('--repo <owner/repo>', 'Repository context')
.option('--issue <number>', 'Issue number for context')
.option('--json', 'Output as JSON')
.action(async (query, opts) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The sage command is tightly coupled to the triage configuration and hardcoded values, which bypasses the new flexible roles configuration system.

  • It uses getTriageConfig() and getTriageApiKey() instead of sourcing configuration from the sage role definition.
  • The error message on line 1038 is hardcoded to ANTHROPIC_API_KEY, which will be incorrect if the user configures a different provider (e.g., OpenAI).
  • The fallback model on line 1043 is hardcoded, whereas the SAGE_ROLE definition already specifies a defaultModel.

To align with the new roles architecture, this command should use getEffectiveRole('sage') to get its configuration and then dynamically determine the provider, model, and API key based on that role's effective configuration.

Comment on lines +1001 to +1006
if (trigger.type === 'comment') {
console.log(` 💬 Comment: ${(trigger as { pattern: string }).pattern}`);
} else if (trigger.type === 'schedule') {
console.log(` ⏰ Schedule: ${(trigger as { cron: string }).cron}`);
} else if (trigger.type === 'event') {
console.log(` 🎯 Events: ${(trigger as { events: string[] }).events.join(', ')}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type assertions like (trigger as { pattern: string }).pattern are redundant. The RoleTrigger type is a discriminated union based on the type property. When you check trigger.type === 'comment', TypeScript's control flow analysis automatically narrows the type of trigger to { type: 'comment'; pattern: string }, so you can safely access trigger.pattern without a cast. Removing these casts makes the code cleaner and relies on the strength of the type system.

Suggested change
if (trigger.type === 'comment') {
console.log(` 💬 Comment: ${(trigger as { pattern: string }).pattern}`);
} else if (trigger.type === 'schedule') {
console.log(` ⏰ Schedule: ${(trigger as { cron: string }).cron}`);
} else if (trigger.type === 'event') {
console.log(` 🎯 Events: ${(trigger as { events: string[] }).events.join(', ')}`);
if (trigger.type === 'comment') {
console.log(` 💬 Comment: ${trigger.pattern}`);
} else if (trigger.type === 'schedule') {
console.log(` ⏰ Schedule: ${trigger.cron}`);
} else if (trigger.type === 'event') {
console.log(` 🎯 Events: ${trigger.events.join(', ')}`);

}

// Apply config overrides if present
const roleConfig = rolesConfig?.[roleId as keyof RolesConfig] as RoleConfig | undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type assertion roleId as keyof RolesConfig is not entirely type-safe, as roleId is a generic string and may not actually be a key of RolesConfig. This can be made safer and clearer by avoiding the direct cast. You can treat rolesConfig as a Record for this lookup to make the intent clearer and slightly safer.

Suggested change
const roleConfig = rolesConfig?.[roleId as keyof RolesConfig] as RoleConfig | undefined;
const roleConfig = (rolesConfig as Record<string, RoleConfig | undefined>)?.[roleId];

],
};
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passed model parameter ignored in main execution path

The executeSageRole function accepts a model: LanguageModel parameter but the primary code path ignores it entirely. Instead, new Analyzer() is created without options, causing it to use its own model configuration from environment/config defaults via resolveProviderOptions(). The passed model is only used in the catch block fallback. This means the CLI carefully configures a model with the correct API key and provider, but that configuration is silently discarded in favor of the Analyzer's defaults.

Fix in Cursor Fix in Web

fixer?: RoleConfig;
/** Delegator role configuration */
delegator?: RoleConfig;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate RolesConfig type missing custom roles field

There are two RolesConfig and RoleConfig type definitions that differ in their properties. The version in config.ts (used by AgenticConfig.roles) is missing the custom field and several RoleConfig properties like triggers, additionalCapabilities, and revokedCapabilities. These features are used by getEffectiveRole and applyRoleConfig in the executor, so users configuring via AgenticConfig won't get proper type hints for these supported features.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread packages/agentic-control/src/cli.ts Outdated

console.log('🔮 Sage is thinking...\n');

const result = await executeSageRole(query, model as Parameters<typeof executeSageRole>[1]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI options for repo and issue context never used

The roles sage command defines --repo and --issue options (lines 1026-1027) but never uses them. The call to executeSageRole on line 1047 only passes query and model, omitting the third context parameter where these options would be passed. The executeSageRole function does support context.issueContext for enriching the query, but this feature is inaccessible from the CLI despite being advertised.

Fix in Cursor Fix in Web

- Add tsup.config.ts to all packages (agentic-control, providers, vitest-agentic-control)
- Update build scripts to use tsup
- Use --no-frozen-lockfile for transition
@jbdevprimary
Copy link
Copy Markdown
Contributor Author

🤖 Agent Handoff: @cursor

Enterprise Dashboard: jbcom/control-center#505

Context

Adds configurable AI agent personas (Sage, Harvester, Curator, etc.) to control package.

Your Responsibilities

  1. Handle ALL AI PR feedback
  2. Wait for feat(build): migrate to tsup for ESM build #42 to merge first (tsup migration)
  3. Rebase onto main after feat(build): migrate to tsup for ESM build #42 merges
  4. Review and merge

Priority

Phase 2 - Feature PRs (after ESM foundation).

Dependency

control#42 (tsup) → merge first
    ↓
control#41 (this) → rebase, then merge

Please respond with status.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
5 Security Hotspots
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add 'sage' CLI command for Q&A, task decomposition, and agent routing

3 participants