App-Dapper
view release on metacpan or search on metacpan
lib/App/Dapper/Agents.pm view on Meta::CPAN
package App::Dapper::Agents;
=head1 NAME
App::Dapper::Agents - Explain a Dapper project to AI coding agents.
=head1 DESCRIPTION
C<dapper init> writes an C<AGENTS.md> file that tells AI coding agents (and
people) how the project works: where pages, layouts and data live, which
front matter each kind of page needs, the rules Dapper enforces, and the
commands to run. A C<CLAUDE.md> points Claude Code at it.
C<dapper agents> brings C<AGENTS.md> up to date after the project changes.
The part Dapper writes sits between C<< <!-- dapper:begin --> >> and
C<< <!-- dapper:end --> >>; anything outside those lines (your own notes, or
a theme's) is kept.
=cut
use utf8;
use 5.010;
use strict;
use warnings;
use File::Basename qw/ dirname /;
use File::Find ();
use App::Dapper::Utils;
use App::Dapper::Schema;
my $BEGIN = '<!-- dapper:begin (this part is written by "dapper agents"; edit outside it) -->';
my $END = '<!-- dapper:end -->';
=head2 update
Takes a Dapper object (and optionally extra notes for a new file), and writes
or updates C<AGENTS.md> in the current directory, and a C<CLAUDE.md> if there
isn't one. Returns the list of files written.
=cut
sub update {
my ($dapper, $notes) = @_;
my $generated = "$BEGIN\n" . guide($dapper) . "$END\n";
my @written;
my $file = 'AGENTS.md';
if (-f $file) {
my $text = App::Dapper::Utils::read_file($file);
if ($text =~ s/\Q$BEGIN\E.*?\Q$END\E\n?/$generated/s) {
App::Dapper::Utils::write_file($file, $text);
}
else {
App::Dapper::Utils::write_file($file, "$text\n$generated");
}
}
else {
my $text = $generated;
$text .= "\n$notes" if defined $notes and length $notes;
App::Dapper::Utils::write_file($file, $text);
}
push @written, $file;
if (not -e 'CLAUDE.md') {
App::Dapper::Utils::write_file('CLAUDE.md',
"This site is built with Dapper. Read AGENTS.md before making changes.\n\n\@AGENTS.md\n");
push @written, 'CLAUDE.md';
}
return @written;
}
=head2 guide
Takes a Dapper object and returns the guide to the project, in Markdown.
=cut
sub guide {
my ($dapper) = @_;
my $loaded = eval {
local $dapper->{quiet} = 1;
local $dapper->{collect} = 1;
$dapper->{problems} = [];
$dapper->{written} = {};
$dapper->{page_owner} = {};
$dapper->parse();
$dapper->transform();
1;
};
my $site = $dapper->{site};
my ($source, $layout, $output, $data) = map { $dapper->{$_} } qw/ source layout output data_dir /;
my $name = defined $site->{name} ? $site->{name} : 'this site';
my $text = <<"MD";
# Working on $name
This is a static website built with [Dapper](https://github.com/markdbenson/dapper).
Pages are Markdown or HTML files with YAML front matter; layouts turn them into
( run in 0.737 second using v1.01-cache-2.11-cpan-145b269f2f3 )