feat(pijul-qol:zero-live-search): polish functionality to get it to atually working. fix errors, make tests thtually work

Chasesomero
Mar 7, 2026, 2:41 PM
RVCFE3CZXAR4W7BCMGCZ2CRNWRFZFYDY7UO2EZGRBAJF6NFYGOVAC

Dependencies

  • [2] WHWKBNW2
  • [3] 32ICZXL4 init laravel zero for fun
  • [4] KGTS53AD feat(init): change laravel zero app name
  • [5] ZHWYHIOH feat(pijul-qol): have copilot make script to show change messages beside changes, in pijul credit output
  • [6] F435FWSF feat(pijul-qol:move-to-zero): move to laravel zero, why not?
  • [7] WNLMHTPQ feat(pijul-qol:move-to-zero): two new commands, to try make intent-tag discovery easier
  • [8] QFA6RRE2 docs(save pr idea for later): save an idea that sounds similar to pr reviewing to try later
  • [9] XUNF6O2C feat(pijul-qol): a record script that surfaces past tags to frontload organization a little

Change contents

  • file deletion: pijultester (---r------)pijultester (---r------)
    [2.1][4.0:35](),[4.35][4.36:36](),[2.1][4.0:35]()
    #!/usr/bin/env php
    <?php
    define('LARAVEL_START', microtime(true));
    /*
    |--------------------------------------------------------------------------
    | Register The Auto Loader
    |--------------------------------------------------------------------------
    |
    | Composer provides a convenient, automatically generated class loader
    | for our application. We just need to utilize it! We'll require it
    | into the script here so that we do not have to worry about the
    | loading of any our classes "manually". Feels great to relax.
    |
    */
    $autoloader = require file_exists(__DIR__.'/vendor/autoload.php') ? __DIR__.'/vendor/autoload.php' : __DIR__.'/../../autoload.php';
    $app = require_once __DIR__.'/bootstrap/app.php';
    /*
    |--------------------------------------------------------------------------
    | Run The Artisan Application
    |--------------------------------------------------------------------------
    |
    | When we run the console application, the current CLI command will be
    | executed in this console and the response sent back to a terminal
    | or another output device for the developers. Here goes nothing!
    |
    */
    $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
    $status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
    );
    /*
    |--------------------------------------------------------------------------
    | Shutdown The Application
    |--------------------------------------------------------------------------
    |
    | Once Artisan has finished running, we will fire off the shutdown events
    | so that any final work may be done by the application before we shut
    | down the process. This is the last thing to happen to the request.
    |
    */
    $kernel->terminate($input, $status);
    exit($status);
  • file deletion: composer.json (----------)
    [2.1][3.306322:306359](),[3.306359][3.306360:306360]()
    {
    "name": "laravel-zero/laravel-zero",
    "description": "The Laravel Zero Framework.",
    "keywords": ["framework", "laravel", "laravel zero", "console", "cli"],
    "homepage": "https://laravel-zero.com",
    "type": "project",
    "license": "MIT",
    "support": {
    "issues": "https://github.com/laravel-zero/laravel-zero/issues",
    "source": "https://github.com/laravel-zero/laravel-zero"
    },
    "authors": [
    {
    "name": "Nuno Maduro",
    "email": "enunomaduro@gmail.com"
    }
    ],
    "require": {
    "php": "^8.2",
    "laravel-zero/framework": "^12.0.2"
    },
    "require-dev": {
    "laravel/pint": "^1.25.1",
    "mockery/mockery": "^1.6.12",
    "pestphp/pest": "^3.8.4|^4.1.2"
    },
    "autoload": {
    "psr-4": {
    "App\\": "app/",
    "Database\\Factories\\": "database/factories/",
    "Database\\Seeders\\": "database/seeders/"
    }
    },
    "autoload-dev": {
    "psr-4": {
    "Tests\\": "tests/"
    }
    },
    "config": {
    "preferred-install": "dist",
    "sort-packages": true,
    "optimize-autoloader": true,
    "allow-plugins": {
    "pestphp/pest-plugin": true
    }
    },
    "minimum-stability": "stable",
    "prefer-stable": true,
    }
    "bin": ["pijultester"]
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 3
    [6.73]
    [6.73]
    use App\Prompts\LiveSuggestPrompt;
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 6
    [6.152]
    [6.152]
    use Laravel\Prompts\Key;
    use Laravel\Prompts\MultiSelectPrompt;
  • replacement in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 33
    [6.1204][6.1204:1247](),[6.1247][7.1965:2072]()
    return match ($process->command) {
    ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
    [6.1204]
    [6.1354]
    return match (true) {
    $process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
  • replacement in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 38
    [6.1502][6.1502:1611]()
    ['pijul', 'record', '-m', 'feat(pijul-qol): add laravel command', 'app'] => Process::result(''),
    [6.1502]
    [6.1611]
    $process->command === ['pijul', 'record', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
    Process::result('', 'Captured Pijul hunks.', 1),
    function () use ($process): void {
    file_put_contents(
    (string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
    <<<'TEXT'
    message = ""
    # Hunks
    1. Edit in "README.md"
    + README change
    2. Edit in "app/Console.php"
    + Console change
    TEXT
    );
    },
    ),
    $process->command === ['pijul', 'record', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
    && ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'feat(pijul-qol): add laravel command'
    && ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 65
    [6.1700]
    [6.1700]
    LiveSuggestPrompt::fake(['f', 'e', 'a', 't', '(', 'p', 'i', 'j', 'u', 'l', '-', 'q', 'o', 'l', ')', Key::ENTER, Key::SPACE, Key::ENTER]);
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 70
    [6.1854][6.1854:1924]()
    ->expectsQuestion('Intent tag (optional)', 'feat(pijul-qol)')
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 72
    [6.2013]
    [6.2013]
    Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app'], 2);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
    && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
    && ! $process->tty
    && $process->timeout === null);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
    && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
    && ! $process->tty
    && $process->timeout === null);
  • replacement in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 86
    [6.2018][6.2018:2085]()
    it('forwards an explicit message without prompting', function () {
    [6.2018]
    [6.2085]
    it('records selected hunks when an explicit message is provided interactively', function () {
  • replacement in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 88
    [6.2140][6.2140:2183](),[6.2183][7.2073:2180]()
    return match ($process->command) {
    ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
    [6.2140]
    [6.2290]
    return match (true) {
    $process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
  • replacement in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 92
    [6.2385][6.2385:2499]()
    ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app'] => Process::result(''),
    [6.2385]
    [6.2499]
    $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
    Process::result('', 'Captured Pijul hunks.', 1),
    function () use ($process): void {
    file_put_contents(
    (string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
    <<<'TEXT'
    message = ""
    # Hunks
    1. Edit in "README.md"
    + README change
    TEXT
    );
    },
    ),
    $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
    && ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'ship it'
    && ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 116
    [6.2588]
    [6.2588]
    MultiSelectPrompt::fake([Key::ENTER]);
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 127
    [6.2868]
    [6.2868]
    Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app'], 2);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
    && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
    && ! $process->tty
    && $process->timeout === null);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
    && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
    && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
    && ! $process->tty
    && $process->timeout === null);
  • edit in tests/Feature/PijulRecordWithIntentTagsCommandTest.php at line 140
    [6.2872]
    it('still forwards the message directly when the command runs non-interactively', function () {
    Process::fake(function (PendingProcess $process) {
    return match ($process->command) {
    ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
    ['message' => 'feat(pijul-qol): first'],
    ], JSON_THROW_ON_ERROR)),
    ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app'] => Process::result(''),
    default => Process::result('', 'Unexpected command.', 1),
    };
    });
    $kernel = app(\Illuminate\Contracts\Console\Kernel::class);
    $artisan = new ReflectionMethod($kernel, 'getArtisan');
    $artisan->setAccessible(true);
    $command = $artisan->invoke($kernel)->find('pijul:record-with-intent-tags');
    $input = new Symfony\Component\Console\Input\ArrayInput([
    'command' => 'pijul:record-with-intent-tags',
    '--message' => 'ship it',
    '--all' => true,
    '--description' => 'desc',
    'prefixes' => ['app'],
    ]);
    $input->setInteractive(false);
    $exitCode = $command->run($input, new Symfony\Component\Console\Output\BufferedOutput);
    expect($exitCode)->toBe(0);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app']
    && $process->environment === []
    && ! $process->tty);
    });
  • file deletion: app.php (----------)
    [3.2704][3.5696:5727](),[3.5727][3.5728:5728]()
    <?php
    return [
    /*
    |--------------------------------------------------------------------------
    | Application Name
    |--------------------------------------------------------------------------
    |
    | This value is the name of your application. This value is used when the
    | framework needs to place the application's name in a notification or
    | any other location as required by the application or its packages.
    |
    */
    /*
    |--------------------------------------------------------------------------
    | Application Version
    |--------------------------------------------------------------------------
    |
    | This value determines the "version" your application is currently running
    | in. You may want to follow the "Semantic Versioning" - Given a version
    | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.
    |
    */
    'version' => app('git.version'),
    /*
    |--------------------------------------------------------------------------
    | Application Environment
    |--------------------------------------------------------------------------
    |
    | This value determines the "environment" your application is currently
    | running in. This may determine how you prefer to configure various
    | services the application utilizes. This can be overridden using
    | the global command line "--env" option when calling commands.
    |
    */
    'env' => 'development',
    /*
    |--------------------------------------------------------------------------
    | Autoloaded Service Providers
    |--------------------------------------------------------------------------
    |
    | The service providers listed here will be automatically loaded on the
    | request to your application. Feel free to add your own services to
    | this array to grant expanded functionality to your applications.
    |
    */
    'providers' => [
    App\Providers\AppServiceProvider::class,
    ],
    ];
    'name' => 'Pijultester',
  • file addition: app.php (----------)
    [3.2704]
    <?php
    return [
    /*
    |--------------------------------------------------------------------------
    | Application Name
    |--------------------------------------------------------------------------
    |
    | This value is the name of your application. This value is used when the
    | framework needs to place the application's name in a notification or
    | any other location as required by the application or its packages.
    |
    */
    'name' => 'Ani',
    /*
    |--------------------------------------------------------------------------
    | Application Version
    |--------------------------------------------------------------------------
    |
    | This value determines the "version" your application is currently running
    | in. You may want to follow the "Semantic Versioning" - Given a version
    | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.
    |
    */
    'version' => app('git.version'),
    /*
    |--------------------------------------------------------------------------
    | Application Environment
    |--------------------------------------------------------------------------
    |
    | This value determines the "environment" your application is currently
    | running in. This may determine how you prefer to configure various
    | services the application utilizes. This can be overridden using
    | the global command line "--env" option when calling commands.
    |
    */
    'env' => 'development',
    /*
    |--------------------------------------------------------------------------
    | Autoloaded Service Providers
    |--------------------------------------------------------------------------
    |
    | The service providers listed here will be automatically loaded on the
    | request to your application. Feel free to add your own services to
    | this array to grant expanded functionality to your applications.
    |
    */
    'providers' => [
    App\Providers\AppServiceProvider::class,
    ],
    ];
  • file addition: composer.json (----------)
    [2.1]
    {
    "name": "laravel-zero/laravel-zero",
    "description": "The Laravel Zero Framework.",
    "keywords": ["framework", "laravel", "laravel zero", "console", "cli"],
    "homepage": "https://laravel-zero.com",
    "type": "project",
    "license": "MIT",
    "support": {
    "issues": "https://github.com/laravel-zero/laravel-zero/issues",
    "source": "https://github.com/laravel-zero/laravel-zero"
    },
    "authors": [
    {
    "name": "Nuno Maduro",
    "email": "enunomaduro@gmail.com"
    }
    ],
    "require": {
    "php": "^8.2",
    "laravel-zero/framework": "^12.0.2"
    },
    "require-dev": {
    "laravel/pint": "^1.25.1",
    "mockery/mockery": "^1.6.12",
    "pestphp/pest": "^3.8.4|^4.1.2"
    },
    "autoload": {
    "psr-4": {
    "App\\": "app/",
    "Database\\Factories\\": "database/factories/",
    "Database\\Seeders\\": "database/seeders/"
    }
    },
    "autoload-dev": {
    "psr-4": {
    "Tests\\": "tests/"
    }
    },
    "config": {
    "preferred-install": "dist",
    "sort-packages": true,
    "optimize-autoloader": true,
    "allow-plugins": {
    "pestphp/pest-plugin": true
    }
    },
    "minimum-stability": "stable",
    "prefer-stable": true,
    "bin": ["ani"]
    }
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 6
    [6.8404]
    [6.8423]
    use App\Prompts\LiveSuggestPrompt;
    use Laravel\Prompts\MultiSelectPrompt;
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 9
    [6.8467]
    [6.8467]
    use RuntimeException;
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 14
    [6.8553]
    [6.8553]
    private const PROMPT_SCROLL = 10;
  • replacement in app/Commands/PijulRecordWithIntentTagsCommand.php at line 71
    [6.10590][6.10590:10706]()
    $this->runPijul($this->recordArguments((string) $message), interactive: $this->input->isInteractive());
    [6.10590]
    [6.10706]
    $this->recordChange((string) $message);
  • replacement in app/Commands/PijulRecordWithIntentTagsCommand.php at line 84
    [6.10983][6.10983:11106]()
    $tag = $tags === []
    ? ''
    : (string) $this->anticipate('Intent tag (optional)', $tags, '');
    [6.10983]
    [6.11106]
    $tag = $this->promptForTag($tags);
  • replacement in app/Commands/PijulRecordWithIntentTagsCommand.php at line 94
    [6.11354][6.11354:11436]()
    $this->runPijul($this->recordArguments($fullMessage), interactive: true);
    [6.11354]
    [6.11436]
    $this->recordChange($fullMessage);
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 118
    [6.13114]
    [6.13114]
    }
    private function recordChange(string $message): void
    {
    if (! $this->input->isInteractive()) {
    $this->runPijul($this->recordArguments($message));
    return;
    }
    $hunks = $this->extractRecordHunks($this->captureRecordTemplate());
    $selectedHunks = $this->promptForHunkSelection($hunks);
    $editorScript = $this->createRecordEditorScript();
    try {
    $editorCommand = $this->recordEditorCommand($editorScript);
    $this->runPijul(
    $this->recordArguments(),
    environment: [
    'EDITOR' => $editorCommand,
    'VISUAL' => $editorCommand,
    'ANI_PIJUL_EDITOR_MODE' => 'apply',
    'ANI_PIJUL_MESSAGE' => $message,
    'ANI_PIJUL_SELECTED_HUNKS' => json_encode($selectedHunks, JSON_THROW_ON_ERROR),
    ],
    disableTimeout: true,
    );
    } finally {
    unlink($editorScript);
    }
    }
    /**
    * @param list<string> $tags
    */
    private function promptForTag(array $tags): string
    {
    if ($tags === []) {
    return '';
    }
    return trim((string) (new LiveSuggestPrompt(
    label: 'Intent tag (optional)',
    options: fn (string $value): array => $this->suggestedTags($tags, $value),
    placeholder: 'Type to filter known tags, or press Enter to skip',
    scroll: self::PROMPT_SCROLL,
    hint: 'Results update as you type. Use arrows to choose, or press Enter on an empty prompt to skip.',
    ))->prompt());
    }
    private function captureRecordTemplate(): string
    {
    $editorScript = $this->createRecordEditorScript();
    $capturePath = tempnam(sys_get_temp_dir(), 'ani-pijul-capture-');
    if ($capturePath === false) {
    unlink($editorScript);
    throw new RuntimeException('Unable to create a temporary file for capturing Pijul hunks.');
    }
    try {
    $result = $this->tryPijul(
    $this->recordArguments(),
    environment: [
    'EDITOR' => $this->recordEditorCommand($editorScript),
    'VISUAL' => $this->recordEditorCommand($editorScript),
    'ANI_PIJUL_EDITOR_MODE' => 'capture',
    'ANI_PIJUL_CAPTURE_PATH' => $capturePath,
    ],
    disableTimeout: true,
    );
    $template = file_get_contents($capturePath);
    if (is_string($template) && $template !== '') {
    return $template;
    }
    $this->ensurePijulSucceeded($result);
    } finally {
    unlink($editorScript);
    if (is_file($capturePath)) {
    unlink($capturePath);
    }
    }
    throw new RuntimeException('Unable to capture Pijul hunks.');
    }
    /**
    * @return list<array{number:int,label:string}>
    */
    private function extractRecordHunks(string $template): array
    {
    preg_match_all('/(?ms)^(?<number>\d+)\.\s.*?(?=^\d+\.\s|\z)/', $template, $matches, PREG_SET_ORDER);
    return array_map(function (array $match): array {
    $lines = preg_split('/\R/', trim($match[0])) ?: [];
    $headline = trim((string) array_shift($lines));
    $detail = '';
    foreach ($lines as $line) {
    $line = trim($line);
    if ($line === '' || str_starts_with($line, 'up ')) {
    continue;
    }
    $detail = $line;
    break;
    }
    return [
    'number' => (int) $match['number'],
    'label' => $detail === ''
    ? $headline
    : sprintf('%s - %s', $headline, $detail),
    ];
    }, $matches);
    }
    /**
    * @param list<array{number:int,label:string}> $hunks
    * @return list<int>
    */
    private function promptForHunkSelection(array $hunks): array
    {
    if ($hunks === []) {
    throw new RuntimeException('No Pijul hunks were found to record.');
    }
    $options = [];
    foreach ($hunks as $hunk) {
    $options[(string) $hunk['number']] = $hunk['label'];
    }
    return array_map(
    static fn (string $value): int => (int) $value,
    (new MultiSelectPrompt(
    label: 'Select hunks to record',
    options: $options,
    default: count($options) === 1 ? array_keys($options) : [],
    scroll: self::PROMPT_SCROLL,
    required: 'Select at least one hunk to record.',
    hint: 'Use space to toggle hunks, Ctrl+A to toggle all, and Enter to continue.',
    ))->prompt(),
    );
    }
    /**
    * @param list<string> $tags
    * @return list<string>
    */
    private function suggestedTags(array $tags, string $query): array
    {
    $query = trim($query);
    if ($query === '') {
    return array_values($tags);
    }
    return array_values(array_filter(
    $tags,
    fn (string $tag): bool => str_contains(strtolower($tag), strtolower($query)),
    ));
    }
    private function createRecordEditorScript(): string
    {
    $path = tempnam(sys_get_temp_dir(), 'ani-pijul-editor-');
    if ($path === false) {
    throw new RuntimeException('Unable to create a temporary Pijul editor helper.');
    }
    $script = <<<'PHP'
    <?php
    $file = $argv[1] ?? null;
    if (! is_string($file) || $file === '') {
    fwrite(STDERR, "Missing Pijul editor file.\n");
    exit(1);
    }
    $mode = getenv('ANI_PIJUL_EDITOR_MODE');
    $template = file_get_contents($file);
    if ($template === false) {
    fwrite(STDERR, "Unable to read the Pijul editor file.\n");
    exit(1);
    }
    if ($mode === 'capture') {
    $capturePath = getenv('ANI_PIJUL_CAPTURE_PATH');
    if (! is_string($capturePath) || $capturePath === '') {
    fwrite(STDERR, "Missing ANI_PIJUL_CAPTURE_PATH.\n");
    exit(1);
    }
    if (file_put_contents($capturePath, $template) === false) {
    fwrite(STDERR, "Unable to capture the Pijul editor file.\n");
    exit(1);
    }
    fwrite(STDERR, "Captured Pijul hunks.\n");
    exit(1);
    }
    if ($mode !== 'apply') {
    fwrite(STDERR, "Unsupported ANI_PIJUL_EDITOR_MODE.\n");
    exit(1);
    }
    $message = getenv('ANI_PIJUL_MESSAGE');
    if (! is_string($message)) {
    fwrite(STDERR, "Missing ANI_PIJUL_MESSAGE.\n");
    exit(1);
    }
    $encodedMessage = json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    if (! is_string($encodedMessage)) {
    fwrite(STDERR, "Unable to encode the Pijul change message.\n");
    exit(1);
    }
    $updated = preg_replace('/^message = .*$/m', 'message = '.$encodedMessage, $template, 1, $count);
    if (! is_string($updated) || $count !== 1) {
    fwrite(STDERR, "Unable to prefill the Pijul change message.\n");
    exit(1);
    }
    $selectedHunks = json_decode((string) getenv('ANI_PIJUL_SELECTED_HUNKS'), true);
    if (! is_array($selectedHunks)) {
    fwrite(STDERR, "Missing ANI_PIJUL_SELECTED_HUNKS.\n");
    exit(1);
    }
    $selected = [];
    foreach ($selectedHunks as $value) {
    if (is_int($value) || (is_string($value) && ctype_digit($value))) {
    $selected[(string) $value] = true;
    }
    }
    if ($selected === []) {
    fwrite(STDERR, "No hunks were selected.\n");
    exit(1);
    }
    if (preg_match('/^\d+\.\s/m', $updated, $firstHunk, PREG_OFFSET_CAPTURE) !== 1) {
    fwrite(STDERR, "Unable to find hunks in the Pijul editor file.\n");
    exit(1);
    }
    $header = rtrim(substr($updated, 0, $firstHunk[0][1]), "\n");
    $body = substr($updated, $firstHunk[0][1]);
    if (preg_match_all('/(?ms)^\d+\.\s.*?(?=^\d+\.\s|\z)/', $body, $matches) !== false) {
    $blocks = [];
    foreach ($matches[0] as $block) {
    if (preg_match('/^(?<number>\d+)\.\s/m', $block, $numberMatch) !== 1) {
    continue;
    }
    if (isset($selected[$numberMatch['number']])) {
    $blocks[] = rtrim($block, "\n");
    }
    }
    if ($blocks === []) {
    fwrite(STDERR, "None of the selected hunks matched the Pijul editor file.\n");
    exit(1);
    }
    $updated = $header."\n\n".implode("\n\n", $blocks)."\n";
    }
    if (file_put_contents($file, $updated) === false) {
    fwrite(STDERR, "Unable to update the Pijul editor file.\n");
    exit(1);
    }
    PHP;
    if (file_put_contents($path, $script) === false) {
    unlink($path);
    throw new RuntimeException('Unable to write the temporary Pijul editor helper.');
    }
    if (! chmod($path, 0700)) {
    unlink($path);
    throw new RuntimeException('Unable to make the temporary Pijul editor helper executable.');
    }
    return $path;
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 429
    [6.13121]
    [6.13121]
    private function recordEditorCommand(string $script): string
    {
    return PHP_BINARY.' '.$script;
    }
  • replacement in app/Commands/PijulRecordWithIntentTagsCommand.php at line 437
    [6.13171][6.13171:13232]()
    private function recordArguments(string $message): array
    [6.13171]
    [6.13232]
    private function recordArguments(?string $message = null): array
  • replacement in app/Commands/PijulRecordWithIntentTagsCommand.php at line 439
    [6.13238][6.13238:13287]()
    $arguments = ['record', '-m', $message];
    [6.13238]
    [6.13287]
    $arguments = ['record'];
  • edit in app/Commands/PijulRecordWithIntentTagsCommand.php at line 441
    [6.13288]
    [6.13288]
    if ($message !== null) {
    $arguments[] = '-m';
    $arguments[] = $message;
    }
  • replacement in app/Commands/Concerns/InteractsWithPijul.php at line 12
    [6.18065][6.18065:18163]()
    protected function runPijul(array $arguments, bool $interactive = false): ProcessResult
    {
    [6.18065]
    [6.18163]
    protected function runPijul(
    array $arguments,
    bool $interactive = false,
    array $environment = [],
    bool $disableTimeout = false,
    ): ProcessResult {
    return $this->ensurePijulSucceeded($this->tryPijul($arguments, $interactive, $environment, $disableTimeout));
    }
    protected function tryPijul(
    array $arguments,
    bool $interactive = false,
    array $environment = [],
    bool $disableTimeout = false,
    ): ProcessResult {
  • edit in app/Commands/Concerns/InteractsWithPijul.php at line 28
    [6.18217]
    [6.18217]
    if ($environment !== []) {
    $pendingProcess = $pendingProcess->env($environment);
    }
  • edit in app/Commands/Concerns/InteractsWithPijul.php at line 33
    [6.18218]
    [6.18218]
    if ($disableTimeout) {
    $pendingProcess = $pendingProcess->forever();
    }
  • replacement in app/Commands/Concerns/InteractsWithPijul.php at line 41
    [6.18340][6.18340:18416]()
    $result = $pendingProcess->run(array_merge(['pijul'], $arguments));
    [6.18340]
    [6.18416]
    return $pendingProcess->run(array_merge(['pijul'], $arguments));
    }
  • edit in app/Commands/Concerns/InteractsWithPijul.php at line 44
    [6.18417]
    [6.18417]
    protected function ensurePijulSucceeded(ProcessResult $result): ProcessResult
    {
  • file addition: ani (---r------)
    [2.1]
    #!/usr/bin/env php
    <?php
    define('LARAVEL_START', microtime(true));
    /*
    |--------------------------------------------------------------------------
    | Register The Auto Loader
    |--------------------------------------------------------------------------
    |
    | Composer provides a convenient, automatically generated class loader
    | for our application. We just need to utilize it! We'll require it
    | into the script here so that we do not have to worry about the
    | loading of any our classes "manually". Feels great to relax.
    |
    */
    $autoloader = require file_exists(__DIR__.'/vendor/autoload.php') ? __DIR__.'/vendor/autoload.php' : __DIR__.'/../../autoload.php';
    $app = require_once __DIR__.'/bootstrap/app.php';
    /*
    |--------------------------------------------------------------------------
    | Run The Artisan Application
    |--------------------------------------------------------------------------
    |
    | When we run the console application, the current CLI command will be
    | executed in this console and the response sent back to a terminal
    | or another output device for the developers. Here goes nothing!
    |
    */
    $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
    $status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
    );
    /*
    |--------------------------------------------------------------------------
    | Shutdown The Application
    |--------------------------------------------------------------------------
    |
    | Once Artisan has finished running, we will fire off the shutdown events
    | so that any final work may be done by the application before we shut
    | down the process. This is the last thing to happen to the request.
    |
    */
    $kernel->terminate($input, $status);
    exit($status);
  • replacement in README.md at line 22
    [6.19398][6.19398:19489]()
    - The intent-tag prompt supports terminal autocompletion from recent `pijul log` messages.
    [6.19398]
    [6.19489]
    - The intent-tag prompt uses the same live suggestion UI as the search command.
    - In an interactive terminal, the command now shows an explicit multi-select hunk picker before recording, so you can choose exactly which hunks to send through without editing Pijul's temp file by hand.
  • edit in README.md at line 32
    [6.19728]
    [5.223]
    ```
    To spin up a disposable repo for manually testing that flow, run:
    ```bash
    ./scripts/pijul-record-with-intent-tags-test-helper
  • edit in README.md at line 39
    [5.227]
    [7.8481]
    It creates a temporary Pijul repo with pending README and notes changes, drops you into a shell there, and removes the repo automatically when you exit.
  • edit in README.md at line 58
    [7.8981]
    [8.0]
    ## Before-push review against a remote
    Pijul does not have a direct equivalent to `git diff origin/main...HEAD`, so the easiest review workflow is to pull the remote channel into a local review channel and diff against that.
    Example:
    ```bash
    pijul pull origin --from-channel main --to-channel review-main
    pijul channel switch my-work
    pijul diff --channel review-main
    ```