OIILX7N5FU73FKQ2OGAH3WHCQWNAC4JZBQE3AUX5EQIV2Z7LSQUAC async function ensureDefaultRemote(repoPath: string, remote: string): Promise<void> {const configPath = path.join(repoPath, '.pijul', 'config');let config = '';if (await pathExists(configPath)) {config = await fs.readFile(configPath, 'utf8');}const entry = `default_remote = "${remote}"`;if (config.trim().length === 0) {await fs.writeFile(configPath, `${entry}\n`, 'utf8');return;}if (config.match(/^default_remote\s*=.*$/m)) {const updated = config.replace(/^default_remote\s*=.*$/m, entry);await fs.writeFile(configPath, updated, 'utf8');return;}const suffix = config.endsWith('\n') ? '' : '\n';await fs.writeFile(configPath, `${config}${suffix}${entry}\n`, 'utf8');}
}const channelResult = await runCommand('pijul', ['channel', 'new', channel, '--no-prompt'], workspace);if (channelResult.exitCode !== 0) {const combined = `${channelResult.stdout}\n${channelResult.stderr}`.toLowerCase();if (!combined.includes('already') && !combined.includes('exists')) {return `Failed to create channel: ${channelResult.stderr || channelResult.stdout}`;}}const switchResult = await runCommand('pijul', ['channel', 'switch', channel, '--no-prompt'], workspace);if (switchResult.exitCode !== 0) {return `Failed to switch channel: ${switchResult.stderr || switchResult.stdout}`;
const syncResult = await runCommand('pijul',['pull', root, '--from-channel', state.trunkChannel, '-a', '--no-prompt'],feature.workspace,);if (syncResult.exitCode !== 0) {return `Failed to sync trunk before push: ${syncResult.stderr || syncResult.stdout}`;}
if (pullResult.exitCode !== 0) {return `Failed to pull feature changes into trunk: ${pullResult.stderr || pullResult.stdout}`;}const trunkAfterPullClean = await pijulDiffIsClean(root);if (!trunkAfterPullClean) {feature.status = 'blocked';await writeState(root, state);return 'Trunk has unresolved changes after pull; resolve conflicts in trunk then retry push.';}if (state.remote) {const pushResult = await runCommand('pijul',['push', state.remote, '--from-channel', state.trunkChannel, '-a', '--no-prompt'],root,);if (pushResult.exitCode !== 0) {return `Failed to push trunk: ${pushResult.stderr || pushResult.stdout}`;}
if (pushResult.exitCode !== 0) {return `Failed to push trunk: ${pushResult.stderr || pushResult.stdout}`;
- The rule: record must always be non-interactive (use `-a`, a message, and `--no-prompt`); never open an editor.- The rule: push must always be non-interactive (use `-a` and `--no-prompt`).- The rule: all work stays on trunk in the forked workspace; use the `parent` remote (default remote points to the parent repo) for sync/push.
async function ensureDefaultRemote(repoPath: string, remote: string): Promise<void> {const configPath = path.join(repoPath, '.pijul', 'config');let config = '';if (await pathExists(configPath)) {config = await fs.readFile(configPath, 'utf8');}const entry = `default_remote = "${remote}"`;if (config.trim().length === 0) {await fs.writeFile(configPath, `${entry}\n`, 'utf8');return;}if (config.match(/^default_remote\s*=.*$/m)) {const updated = config.replace(/^default_remote\s*=.*$/m, entry);await fs.writeFile(configPath, updated, 'utf8');return;}const suffix = config.endsWith('\n') ? '' : '\n';await fs.writeFile(configPath, `${config}${suffix}${entry}\n`, 'utf8');}
return `${feature.feature} (${feature.status}) depth=${depth} parent=${parent} channel=${feature.channel} workspace=${feature.workspace}`;
return `${feature.feature} (${feature.status}) depth=${depth} parent=${parent} workspace=${feature.workspace}`;
const channelResult = await runCommand('pijul', ['channel', 'new', channel, '--no-prompt'], workspace);if (channelResult.exitCode !== 0) {const combined = `${channelResult.stdout}\n${channelResult.stderr}`.toLowerCase();if (!combined.includes('already') && !combined.includes('exists')) {return `Failed to create channel: ${channelResult.stderr || channelResult.stdout}`;}}const switchResult = await runCommand('pijul', ['channel', 'switch', channel, '--no-prompt'], workspace);if (switchResult.exitCode !== 0) {return `Failed to switch channel: ${switchResult.stderr || switchResult.stdout}`;}
}const switchResult = await runCommand('pijul', ['channel', 'switch', state.trunkChannel, '--no-prompt'], root);if (switchResult.exitCode !== 0) {return `Failed to switch trunk channel: ${switchResult.stderr || switchResult.stdout}`;}const pullResult = await runCommand('pijul',['pull', feature.workspace, '--from-channel', feature.channel, '-a', '--no-prompt'],root,);if (pullResult.exitCode !== 0) {return `Failed to pull feature changes into trunk: ${pullResult.stderr || pullResult.stdout}`;}const trunkAfterPullClean = await pijulDiffIsClean(root);if (!trunkAfterPullClean) {feature.status = 'blocked';await writeState(root, state);return 'Trunk has unresolved changes after pull; resolve conflicts in trunk then retry push.';}if (state.remote) {const pushResult = await runCommand('pijul',['push', state.remote, '--from-channel', state.trunkChannel, '-a', '--no-prompt'],root,);if (pushResult.exitCode !== 0) {return `Failed to push trunk: ${pushResult.stderr || pushResult.stdout}`;}
if (featureSwitchResult.exitCode !== 0) {return `Failed to switch feature workspace to trunk: ${featureSwitchResult.stderr || featureSwitchResult.stdout}`;
if (pushResult.exitCode !== 0) {return `Failed to push trunk: ${pushResult.stderr || pushResult.stdout}`;
const deleteResult = await runCommand('pijul',['channel', 'delete', feature.channel, '--no-prompt'],feature.workspace,);if (deleteResult.exitCode !== 0) {const combined = `${deleteResult.stdout}\n${deleteResult.stderr}`.toLowerCase();if (!combined.includes('not found')) {return `Failed to delete feature channel: ${deleteResult.stderr || deleteResult.stdout}`;}}
- The workflow: sync → resolve conflicts → record → push → verify trunk clean → delete channel → report summary.
- The workflow: sync → resolve conflicts → record → push → verify trunk clean → report summary.- The rule: all work stays on trunk in the forked workspace; use the `parent` remote (default remote points to the parent repo) for sync/push.- The rule: record must always be non-interactive (use `-a`, a message, and `--no-prompt`); never open an editor.- The rule: push must always be non-interactive (use `-a` and `--no-prompt`).
You are a feature sub-agent. Push your ready feature into trunk after syncing and recording.
You are a feature sub-agent. Push your ready feature into trunk on the `parent` remote (default remote points to the parent repo) after syncing and recording.CRITICAL: Never allow an editor to open. Always push non-interactively with `-a` and `--no-prompt`.
Remind the sub-agent of the workflow: sync → resolve conflicts → record → push → verify trunk clean → delete channel → report summary.
Remind the sub-agent of the workflow: sync → resolve conflicts → record → push → verify trunk clean → report summary.