App-karr
view release on metacpan or search on metacpan
lib/App/karr/Cmd/SetRefs.pm view on Meta::CPAN
option dir => (
is => 'ro',
format => 's',
doc => 'Path used as the starting point for Git repository discovery',
predicate => 1,
);
sub execute {
my ($self, $args_ref, $chain_ref) = @_;
my ($ref_input, @content_parts) = $self->positional_args($args_ref);
die "Usage: karr set-refs REF CONTENT...\n" unless defined $ref_input;
my $repo_dir = '.';
if ($self->has_dir) {
$repo_dir = $self->dir;
}
elsif ($chain_ref && @$chain_ref) {
my $root = $chain_ref->[0];
if ($root && $root->can('has_dir') && $root->has_dir) {
$repo_dir = $root->dir;
}
}
my $git = App::karr::Git->new(dir => $repo_dir);
die "Not a git repository.\n" unless $git->is_repo;
# for_write: a helper ref may be read from every namespace it may not be
# written to. refs/karr-foundation/chain/ and .../log/ are karr-foundation's
# own structured state, and this command writes last-writer-wins with its
# arguments joined by a space, which is not how a chain step is updated.
my $ref = $git->validate_helper_ref( $ref_input, for_write => 1 );
# Arguments join with a space -- one line, which is what a hint or a status
# word is. A document is not that shape, and handing one over the obvious way
# (a heredoc, an unquoted paste) used to collapse every newline into a space
# and store the result without a word (#195). Reading stdin when there is
# nothing to join is the addition that costs no existing caller anything:
# that argv shape was a usage error before, so no invocation that worked
# changes meaning -- whereas joining with newlines instead would silently
# rewrite the payload of every multi-word call, including the one this
# command's own SYNOPSIS, the README and the packaged skill all teach.
my $content = @content_parts
? join( ' ', @content_parts )
: $self->_payload_from_stdin;
$git->write_ref($ref, $content) or die "Failed to write $ref\n";
$git->push_ref($ref) or die "Failed to push $ref\n";
print STDERR "Stored $ref\n";
}
# The payload as it arrives on stdin, as characters.
#
# STDIN is the one input edge App::karr::Encoding leaves without a PerlIO layer
# (App::karr::Cmd::Restore reads it the same way), so the decode is explicit and
# happens exactly once.
sub _payload_from_stdin {
my ($self) = @_;
# A terminal has nothing queued and would just sit there with no prompt, so
# the shape that was a usage error before stays one instead of becoming a
# hang.
die "Usage: karr set-refs REF CONTENT...\n" if -t STDIN;
binmode STDIN, ':raw';
my $content = do { local $/; <STDIN> };
# An empty stdin is not an empty payload: `karr set-refs REF < /dev/null`, or
# a generator upstream that produced nothing, is a mistake, and storing '' for
# it would report success. The deliberate way to store an empty payload is
# still `karr set-refs REF ""`. A runtime failure (exit 1) rather than a usage
# error, on the same reading as App::karr::Cmd::Restore's empty stdin: the
# invocation was right, what arrived on the pipe was not.
die "No payload on stdin. Pass CONTENT as arguments or pipe a payload in.\n"
unless defined $content && length $content;
return from_octets($content);
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::karr::Cmd::SetRefs - Store helper payloads in a Git ref
=head1 VERSION
version 0.600
=head1 SYNOPSIS
karr set-refs superpowers/spec/1234.md draft ready
karr set-refs refs/superpowers/spec/1234.md "full payload"
karr set-refs superpowers/spec/1234.md < design.md
karr set-refs superpowers/spec/1234.md "payload" --dir /path/to/repo
=head1 DESCRIPTION
Writes a helper payload into a free-form Git ref outside the protected board
namespace. This is intended for adjunct workflow data such as AI planning
artifacts or coordination hints that should sync through Git without becoming a
task card.
The payload is every argument after C<REF>, joined with a single space. That is
a one-line shape on purpose, and it is the whole payload: a multi-line document
handed over as several arguments would come back as one long line. So a
document is piped instead -- with no C<CONTENT> argument at all the payload is
read from standard input verbatim, newlines and all, and
C<< karr set-refs REF E<lt> file >> round-trips through
C<< karr get-refs REF E<gt> file >>. Stdin is only read when there is nothing
to join, so an argument form that worked before is untouched, and a bare
C<karr set-refs REF> at a terminal is still the usage error it always was
rather than a command that sits there waiting.
( run in 1.712 second using v1.01-cache-2.11-cpan-aadc1410aed )