Skip to content

Commit 6644144

Browse files
committed
refactor(config): auto-update existing agent instructions instead of prompting
vp config now silently updates agent instruction files that contain Vite+ markers when content is outdated. Never creates new agent files. Same behavior for prepare and manual runs. - Add updateExistingAgentInstructions() to utils/agent.ts - Simplify config/bin.ts to a single update call (no interactive/prepare guards) - Add snap tests for auto-update and no-write scenarios
1 parent ea90216 commit 6644144

11 files changed

Lines changed: 108 additions & 35 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# My Custom Claude Instructions
2+
3+
Do not modify this file.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "command-config-no-agent-writes"
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
> git init
2+
> vp config
3+
> cat CLAUDE.md # should be unchanged
4+
# My Custom Claude Instructions
5+
6+
Do not modify this file.
7+
8+
> test -f AGENTS.md && echo 'AGENTS.md exists' || echo 'AGENTS.md not created' # should not exist
9+
AGENTS.md not created
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"commands": [
3+
{ "command": "git init", "ignoreOutput": true },
4+
"vp config",
5+
"cat CLAUDE.md # should be unchanged",
6+
"test -f AGENTS.md && echo 'AGENTS.md exists' || echo 'AGENTS.md not created' # should not exist"
7+
]
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# My Project
2+
3+
Custom instructions here.
4+
5+
<!--VITE PLUS START-->
6+
7+
OUTDATED CONTENT THAT SHOULD BE REPLACED
8+
9+
<!--VITE PLUS END-->
10+
11+
More custom content below.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "command-config-update-agents"
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
> git init
2+
> vp config # should auto-update agent instructions
3+
> head -5 AGENTS.md # verify user content preserved
4+
# My Project
5+
6+
Custom instructions here.
7+
8+
<!--VITE PLUS START-->
9+
10+
> tail -3 AGENTS.md # verify user content preserved
11+
<!--VITE PLUS END-->
12+
13+
More custom content below.
14+
15+
> grep -q 'OUTDATED CONTENT' AGENTS.md && echo 'ERROR: outdated content still present' || echo 'outdated content replaced' # verify old content gone
16+
outdated content replaced
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"commands": [
3+
{ "command": "git init", "ignoreOutput": true },
4+
"vp config # should auto-update agent instructions",
5+
"head -5 AGENTS.md # verify user content preserved",
6+
"tail -3 AGENTS.md # verify user content preserved",
7+
"grep -q 'OUTDATED CONTENT' AGENTS.md && echo 'ERROR: outdated content still present' || echo 'outdated content replaced' # verify old content gone"
8+
]
9+
}

packages/cli/src/config/bin.ts

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
// Unified `vp config` command — merges the old `vp prepare` (hooks setup) and
2-
// `vp init` (agent integration) into a single entry point.
1+
// Unified `vp config` command — hooks setup + agent instruction updates.
32
//
4-
// Interactive mode (TTY, no CI): prompts on first run; prompts for agent setup
5-
// only if no agent instruction files exist.
6-
// Non-interactive mode (scripts.prepare, CI, piped): hooks only, agent setup skipped.
3+
// Hooks: interactive mode prompts on first run; non-interactive installs by default.
4+
// Agent instructions: silently updates existing files with Vite+ markers.
5+
// Never creates new agent files. Same behavior for prepare and manual runs.
76

87
import { existsSync } from 'node:fs';
98
import { join } from 'node:path';
109

1110
import mri from 'mri';
1211

1312
import { vitePlusHeader } from '../../binding/index.js';
14-
import {
15-
detectExistingAgentTargetPaths,
16-
selectAgentTargetPaths,
17-
writeAgentInstructions,
18-
} from '../utils/agent.js';
13+
import { updateExistingAgentInstructions } from '../utils/agent.js';
1914
import { renderCliDoc } from '../utils/help.js';
2015
import { defaultInteractive, promptGitHooks } from '../utils/prompts.js';
2116
import { log } from '../utils/terminal.js';
@@ -80,27 +75,9 @@ async function main() {
8075
}
8176
}
8277

83-
// --- Step 2: Agent setup (interactive only, skipped if files exist / --hooks-only / prepare) ---
84-
if (!hooksOnly && interactive && process.env.npm_lifecycle_event !== 'prepare') {
85-
// Skip entirely if any agent instruction files already exist
86-
const existingAgentTargetPaths = detectExistingAgentTargetPaths(root);
87-
if (existingAgentTargetPaths === undefined) {
88-
// No existing agent files — prompt for selection
89-
const selectedAgentTargetPaths = await selectAgentTargetPaths({
90-
interactive,
91-
onCancel: () => {
92-
process.exit(0);
93-
},
94-
});
95-
96-
if (selectedAgentTargetPaths) {
97-
await writeAgentInstructions({
98-
projectRoot: root,
99-
targetPaths: selectedAgentTargetPaths,
100-
interactive,
101-
});
102-
}
103-
}
78+
// --- Step 2: Update agent instructions if Vite+ header exists and is outdated ---
79+
if (!hooksOnly) {
80+
updateExistingAgentInstructions(root);
10481
}
10582
}
10683

packages/cli/src/utils/agent.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,40 @@ export function hasExistingAgentInstructions(projectRoot: string): boolean {
280280
return false;
281281
}
282282

283+
/**
284+
* Silently update agent instruction files that contain Vite+ markers.
285+
* - No agent files → no writes
286+
* - No Vite+ markers → no writes
287+
* - Markers present, content up to date → no writes
288+
* - Markers present, content outdated → update marked section
289+
*/
290+
export function updateExistingAgentInstructions(projectRoot: string): void {
291+
const targetPaths = detectExistingAgentTargetPaths(projectRoot);
292+
if (!targetPaths) {
293+
return;
294+
}
295+
296+
const templatePath = path.join(pkgRoot, 'AGENTS.md');
297+
if (!fs.existsSync(templatePath)) {
298+
return;
299+
}
300+
301+
const templateContent = fs.readFileSync(templatePath, 'utf-8');
302+
303+
for (const targetPath of targetPaths) {
304+
try {
305+
const fullPath = path.join(projectRoot, targetPath);
306+
const existing = fs.readFileSync(fullPath, 'utf-8');
307+
const updated = replaceMarkedAgentInstructionsSection(existing, templateContent);
308+
if (updated !== undefined && updated !== existing) {
309+
fs.writeFileSync(fullPath, updated);
310+
}
311+
} catch {
312+
// Best-effort: skip files that can't be read or written
313+
}
314+
}
315+
}
316+
283317
export function resolveAgentTargetPaths(agent?: string | string[]) {
284318
const agentNames = parseAgentNames(agent);
285319
const resolvedAgentNames = agentNames.length > 0 ? agentNames : ['other'];

0 commit comments

Comments
 (0)