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 )