App-karr
view release on metacpan or search on metacpan
lib/App/karr/Foundation.pm view on Meta::CPAN
return $self->_claude_command($karr) if $claude;
return undef;
}
# Synthesize the canonical claude invocation behind 'claude: true'. The $PROMPT
# variable is substituted from $ENV{PROMPT} at run time (see _run_command), so
# users never retype the long flag set. claude_bin / claude_max_turns /
# claude_permission_mode override the defaults (per-repo, then global).
sub _claude_command {
my ( $self, $karr ) = @_;
my $cfg = $self->_config_data;
my $bin = $karr->{claude_bin} // $cfg->{claude_bin} // 'claude';
my $turns = $karr->{claude_max_turns} // $cfg->{claude_max_turns} // 30;
my $perm = $karr->{claude_permission_mode} // $cfg->{claude_permission_mode} // 'bypassPermissions';
return qq{$bin -p "\$PROMPT" --permission-mode $perm --max-turns $turns};
}
# The agent instruction exposed as $PROMPT. .karr 'prompt' > config
# 'default_prompt' > the built-in default.
sub _prompt_for {
my ( $self, $karr ) = @_;
return $karr->{prompt}
// $self->_config_data->{default_prompt}
// $DEFAULT_PROMPT;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::karr::Foundation - Single-shot foundation daemon â periodic agent execution across karr boards
=head1 VERSION
version 0.401
=head1 SYNOPSIS
# Typical cron entry â run every 5 minutes
*/5 * * * * /path/to/karr-foundation
# Force a run regardless of board state
karr-foundation --force
# Preview what would run
karr-foundation --dry-run --verbose
# Read-only overview of every board (no agent runs)
karr-foundation --status
=head1 DESCRIPTION
F<karr-foundation> is a single-shot, idempotent CLI meant to be invoked
periodically (cron, systemd-timer, while-loop). It scans configured karr
boards, detects changes or open work, and B<drains> each board by invoking the
configured agent command repeatedly until no actionable task remains.
B<Config file:> C<~/.config/karr-foundation/config.yml> (or C<--config>).
dirs:
- /path/to/repo1
- /path/to/repo2
scan:
- /path/to/parent-dir # finds all direct subdirs that have a .karr file
B<Per-repo .karr file:>
claude: true # synthesize the canonical claude command (opt-in)
claude_bin: claude # binary for claude: true (default: claude)
claude_max_turns: 30 # --max-turns for claude: true (default: 30)
claude_permission_mode: bypassPermissions # (default: bypassPermissions)
prompt: >- # agent instruction, exposed as $PROMPT
Use the karr-coordinator skill: pick the next actionable task and move it.
command: claude -p "$PROMPT" # explicit command; wins over claude: true
on_idle: skip # 'skip' (default) | 'always-run'
max_runtime: 1800 # seconds: per-command SIGKILL (0 = no limit)
drain: true # loop until drained (default) | false for single run
max_attempts: 2 # stalls on one task before auto-block (default: 2)
max_iterations: 50 # hard cap on drain iterations (default: 50)
cooldown_base: 1 # cooldown minutes at level 0 (default: 1)
cooldown_max: 64 # cooldown ceiling in minutes (default: 64)
error_patterns: # extra case-insensitive substrings â common-error
- my custom api error
C<claude>, C<claude_bin>, C<claude_max_turns>, C<claude_permission_mode>,
C<command> and C<prompt>/C<default_prompt> may also be set globally in the
config file; the per-repo F<.karr> value wins.
B<Coordinator and overview.> Agent execution is opt-in â a board runs an agent
only via C<command> or C<< claude: true >>. When B<no> board has an agent
configured, the default action is a read-only B<overview> of every board
(status counts, in-progress/blocked tasks, lock and cooldown state); a human
can use foundation purely to coordinate their own work. C<--status> forces the
overview regardless of configuration.
B<Live output.> When run interactively (TTY) or with C<--verbose>, the agent's
output is streamed to the terminal in real time as foundation reads it; it is
always appended to F<.karr.log> regardless of TTY. To shape what is shown, the
command may emit stream-json and filter it, e.g.:
command: >-
claude -p "$PROMPT"
--output-format stream-json --verbose --include-partial-messages
--permission-mode bypassPermissions --max-turns 10
2>&1 | jq -r 'select(.type == "stream_event") | .event.delta.text // empty'
Set C<max_runtime: 0> in F<.karr> to disable the per-run timeout entirely
(agent runs until completion with no SIGKILL).
B<Drain semantics.> Each iteration runs C<command> once, then classifies the
result from what foundation can observe â exit code, board ref movement, and
the run's captured output:
( run in 1.952 second using v1.01-cache-2.11-cpan-817d5f8af8b )