Agent Skills in Claude Code and Visual Studio Code: Setup, Benefits, Risks, and a Step-by-Step Example

Image
 AI coding assistants are useful for one-off questions, but many development tasks are not one-off tasks. A team may follow the same testing procedure before every release, use a specific format for code reviews, or require certain checks whenever a database migration is created. Repeating those requirements in every prompt is inefficient. Putting all of them into a permanent instruction file is not always ideal either, because the assistant may receive irrelevant information for tasks that do not need it. This is the problem that Agent Skills are designed to solve. A skill packages reusable instructions, supporting files, examples, and optional scripts into a folder. Claude Code or GitHub Copilot in Visual Studio Code can discover that folder and load its contents when the current task matches the skill’s purpose. Both environments support the open Agent Skills format, which makes it possible to share some skills across tools. This guide explains what skills are, how they differ ...

What to Do When Your AI Coding Session Goes Off Track



An AI coding session can begin with a small task and gradually become difficult to control.

You ask the assistant to add a filter. It changes the rendering function, rewrites the data model, adds a utility file, modifies the CSS, and suggests installing a package. You then ask it to fix one unexpected result, but the correction creates another problem.

After several prompts, the project may still run, but you no longer know which changes are necessary or which version was last reliable.

This is not unusual in AI-assisted development.

The correct response is not to keep sending increasingly urgent prompts. Stop the session, inspect the repository, preserve the work you trust, and return the project to a state you can explain.

This article shows how to recognize a session that has gone off track and recover without losing verified progress.

What You Will Learn

By the end of this guide, you should be able to:

  • recognize early signs of context drift;
  • distinguish a code bug from a broken development session;
  • stop an AI tool before the scope expands further;
  • inspect the repository without relying on the AI summary;
  • preserve verified changes while discarding unwanted ones;
  • restore individual files safely;
  • recover from uncommitted and committed changes;
  • restart the task with a smaller prompt;
  • decide when to continue, revert, or begin from a clean checkpoint;
  • prevent future sessions from drifting as quickly.

What It Means for a Session to Go Off Track

A session has gone off track when the conversation and repository no longer have a clear relationship to the original task.

Suppose the task was:

Add status filtering to the Personal Reading List Tracker.

Requirements:
- Filter by All, Want to Read, Reading, and Finished.
- Do not modify stored books.
- Do not add dependencies.
- Modify only app.js.

A controlled session should produce a focused change in app.js.

An off-track session may instead produce:

modified: app.js
modified: index.html
modified: styles.css
modified: package.json
modified: package-lock.json
untracked: src/filter-manager.js
untracked: src/state-store.js

The AI may explain that the additional architecture will “make future features easier.”

That may or may not be true. It does not match the agreed task.

The central problem is no longer just whether filtering works. The problem is that the change boundary has disappeared.

Common Signs of Context Drift

Context drift occurs when the AI’s working assumptions gradually move away from the current project state or original requirement.

Watch for these signs.

The AI Refers to Files That Do Not Exist

For example:

I updated BookList.jsx and the Zustand store.

Your project uses plain HTML, CSS, and JavaScript. There is no React component or state-management library.

The assistant may be carrying assumptions from another common project pattern.

The Same Requirement Is Interpreted Differently Over Time

Early in the session, the status values are:

want-to-read
reading
finished

Later, the AI begins using:

to-read
in-progress
completed

Both sets may seem reasonable, but mixing them creates bugs.

Fixes Become Broader Than the Original Feature

You report that one filter does not work.

The AI proposes to:

  • replace the rendering system;
  • rename all status values;
  • convert the app to modules;
  • move state into a class;
  • add a package;
  • rewrite the HTML.

A broad rewrite may be appropriate in some projects, but it should not happen automatically during a small bug fix.

The AI Repeats a Disproven Explanation

You confirm that the selector exists, but the assistant continues claiming the selector is missing.

This suggests it is reasoning from old or incomplete context.

The Assistant Changes Files After Being Told Not To

The prompt says:

Modify only app.js.

The tool also changes index.html and styles.css.

One accidental change can be reviewed. Repeated boundary violations indicate the session should stop.

You No Longer Understand the Current Diff

This is the most important warning sign.

Even when the project appears to work, stop if you cannot explain:

  • which files changed;
  • why each change was necessary;
  • which behavior was verified;
  • which version was last stable.

A working demo is not enough when the path to that demo is unclear.

A Bug Is Not Always a Broken Session

One failing feature does not automatically mean the session is lost.

A normal bug may have:

  • clear reproduction steps;
  • one likely cause;
  • a small relevant diff;
  • a known starting checkpoint;
  • a focused proposed fix.

A broken session usually has several of these problems:

  • multiple unrelated edits;
  • changing assumptions;
  • several speculative fixes;
  • unexplained files;
  • unclear package changes;
  • no reliable test result;
  • no known good state;
  • a conversation that references outdated code.

The distinction matters.

A bug should be debugged.

A broken session should first be stabilized.

Step 1: Stop Generating More Changes

When the session drifts, do not send:

Try again and fix everything.

That adds another layer of assumptions.

Instead, stop editing.

Tell the AI:

Stop making changes. Do not edit files or run commands. Summarize the original task, list every file you believe you changed, and identify any unresolved issues.

Treat the response as a clue, not as the official record.

The actual repository state must be checked with Git.

If the coding tool is currently requesting permission to run another command or modify another file, deny the request until you understand the current state.

Step 2: Inspect the Repository Yourself

Run:

git status

Record:

  • modified tracked files;
  • staged files;
  • untracked files;
  • deleted files;
  • the current branch.

Then run:

git diff --stat

This gives a summary of how many files and lines changed.

For example:

app.js              | 86 +++++++++++++++++++++++------------
index.html           | 14 ++++++
styles.css           | 42 +++++++++++++++++
package.json         |  6 +++
package-lock.json    | 93 +++++++++++++++++++++++++++++++++++++
5 files changed, 196 insertions(+), 45 deletions(-)

A filtering feature that changes nearly two hundred lines across five files deserves careful review.

Now inspect the full diff:

git diff

For specific files:

git diff app.js
git diff index.html
git diff styles.css
git diff package.json

Do not begin deciding what to keep until you know what changed.

Step 3: Compare the Repository with the Original Task

Return to the original prompt and project brief.

Create a small comparison table in your notes:

Original task:
Add status filtering.

Allowed files:
app.js

Required behavior:
- All
- Want to Read
- Reading
- Finished
- Do not modify stored data

Prohibited:
- No dependencies
- No data-model changes
- No unrelated styling

Now compare each changed file.

app.js

Potentially relevant because filtering belongs here.

index.html

Possibly relevant only if the filter control did not already exist.

styles.css

May be unnecessary unless visible filter-state styling was explicitly requested.

package.json

Likely unrelated because filtering a JavaScript array requires no package.

package-lock.json

Likely created because a dependency was installed.

This comparison helps separate necessary work from session drift.

Step 4: Identify the Last Known Good State

Run:

git log --oneline -5

For example:

2ac7e31 Add book removal
fd81b26 Validate book titles
c5a4d77 Add books through the form
1ee7aa2 Add book list rendering

If the working tree was clean before the filtering task, the latest commit is your recovery point.

Confirm what that version contained:

git show --stat 2ac7e31

You may also inspect the previous version of a file:

git show HEAD:app.js

This prints the committed version of app.js without changing the working tree.

You now have two states:

HEAD:
Last committed and verified version

Working tree:
Current uncommitted AI-generated changes

The goal is to decide which parts of the working tree deserve to survive.

Step 5: Preserve Anything You Trust

Do not discard all changes automatically if part of the work is correct and understandable.

Suppose the AI added this focused filtering function:

function getVisibleBooks() {
  if (activeFilter === "all") {
    return books;
  }

  return books.filter((book) => book.status === activeFilter);
}

The function matches the project brief and is easy to test.

The same session also added a package and rewrote the styles.

You may want to preserve only the relevant JavaScript.

There are several ways to do this.

Save a Temporary Patch

Create a patch of the current changes:

git diff > ai-session-changes.patch

This creates a text record of the diff.

Do not commit the patch to the project unless you have a specific reason. It is a temporary recovery aid.

Copy a Small Verified Section into Notes

For a very small change, copy the exact function into a temporary note outside the repository.

Do not copy secrets or private data.

Stage Only the Trusted Part

Interactive staging allows you to choose sections of a file:

git add -p app.js

Git will show one section at a time and ask whether to stage it.

Common responses include:

y = stage this section
n = do not stage this section
s = split into smaller sections
q = stop

Interactive staging is powerful, but review every section carefully.

After staging the trusted portion:

git diff --staged

The staged diff should contain only the work you intend to keep.

Step 6: Restore Unwanted Tracked Changes

For a tracked file whose changes are entirely unwanted:

git restore styles.css

This restores the working copy from the current committed version.

For package changes:

git restore package.json package-lock.json

Review the diff before restoring because uncommitted changes will be discarded.

For a file containing both useful and unwanted edits, use interactive restore:

git restore -p app.js

Git will let you choose which change sections to discard.

This is useful when one AI session mixed valid filtering logic with unrelated refactoring.

Step 7: Remove Unwanted Untracked Files Carefully

Untracked files are not restored by Git because Git has never recorded them.

List them with:

git status

Inspect each one before removal:

cat src/filter-manager.js

For a longer source file, open it in the editor instead.

When you are certain the file is unnecessary:

rm src/filter-manager.js

If a newly created directory contains several files, inspect the directory first:

find src -maxdepth 2 -type f

Do not run broad deletion commands unless you understand exactly what they will remove.

Avoid using cleanup commands merely because the AI recommends them.

Step 8: Decide Whether to Keep a Partial Change

A partial change is worth keeping when:

  • it directly supports the original task;
  • you can explain it;
  • it follows the project brief;
  • it does not depend on discarded code;
  • it can be tested independently;
  • it produces a focused diff.

For the reading list filter, a safe partial result might include:

let activeFilter = "all";

function getVisibleBooks() {
  if (activeFilter === "all") {
    return books;
  }

  return books.filter((book) => book.status === activeFilter);
}

It may also update renderBooks() to render the result of getVisibleBooks().

That can be tested without keeping a package, new architecture, or unrelated design changes.

Reject a partial change when it depends on missing pieces or remains difficult to explain.

Step 9: Test the Recovered State

After removing unwanted changes, run:

git status
git diff

The remaining diff should be small and intentional.

Now test the application.

For status filtering:

1. Add one Want to Read book.
2. Add one Reading book.
3. Add one Finished book.
4. Select each filter.
5. Confirm only matching books appear.
6. Select All.
7. Confirm all books appear.
8. Confirm filtering does not delete any book.

Also test existing behavior:

  • adding books;
  • rejecting blank titles;
  • removing books;
  • showing the empty state.

A recovered change is not ready merely because the diff is smaller. It still needs verification.

Step 10: Commit the Verified Recovery

If the remaining work is correct, stage it specifically:

git add app.js

Review:

git diff --staged

Commit:

git commit -m "Filter books by reading status"

Then confirm:

git status

The working tree should be clean.

You have now converted a confused AI session into a focused, verified project change.

When to Discard the Entire Session

Sometimes none of the generated work is worth preserving.

Discard the session when:

  • the intended feature does not work;
  • the changes are too interconnected to separate;
  • the AI replaced the project architecture;
  • new dependencies are unexplained;
  • you cannot identify which part is correct;
  • the current diff is larger than rebuilding the feature;
  • multiple files contain contradictory versions;
  • the session introduced security or privacy concerns.

For uncommitted tracked changes, inspect first:

git diff

Then restore the tracked files:

git restore app.js index.html styles.css package.json package-lock.json

Remove unwanted untracked files individually after inspection.

Finally:

git status

The repository should return to the last committed checkpoint.

Starting again from a clean state is often faster than repairing code you do not trust.

Recovering from a Bad Commit

The session may have been committed before you realized it was off track.

The safest response depends on whether the commit has been shared.

When the Commit Has Not Been Shared

You may still choose to create a new reversing commit rather than rewriting history, especially as a beginner.

The clear and generally safe option is:

git revert <commit-hash>

This creates a new commit that reverses the selected commit.

For example:

git revert a81c5d2

Git may open an editor for the revert commit message.

Afterward, inspect:

git log --oneline -5
git status

When the Commit Has Already Been Pushed

Use git revert rather than casually rewriting shared history.

The commit remains visible in history, and the new revert commit records that its changes were undone.

This is useful for collaboration because other repositories can follow the same history.

Avoid Destructive Reset Commands as a Default

A command such as:

git reset --hard

can discard local work.

Do not use it casually during a confusing session.

Prefer targeted restoration or a revert commit when possible.

Example Recovery: The Filtering Session Expanded Too Far

Consider this scenario.

Original Task

Add status filtering in app.js.

AI Session Result

The AI:

  • changed app.js;
  • rewrote the filter markup in index.html;
  • redesigned controls in styles.css;
  • installed a state-management package;
  • created src/store.js;
  • renamed status values;
  • modified the README.

Step 1: Stop

No more edits or commands are approved.

Step 2: Inspect

git status
git diff --stat
git diff

Step 3: Compare with the Brief

The project brief requires plain JavaScript and no new dependency.

The package and store file violate those constraints.

Step 4: Preserve Useful Logic

The filtering function in app.js is understandable and matches the original status values.

Use:

git add -p app.js

to stage only the relevant sections.

Step 5: Restore Unwanted Changes

git restore index.html styles.css package.json package-lock.json README.md
rm src/store.js

Step 6: Review Staged Work

git diff --staged

The staged diff now contains only:

  • activeFilter;
  • the filtering function;
  • the filter event handler;
  • the renderer update.

Step 7: Test

Confirm all four filter options and existing Add and Remove behavior.

Step 8: Commit

git commit -m "Filter books by reading status"

The project keeps the useful result while rejecting the uncontrolled expansion.

Restart with a Fresh Prompt

After stabilizing the repository, do not continue the same confused conversation automatically.

Start a fresh AI session when the prior context contains:

  • outdated file names;
  • discarded architecture;
  • failed theories;
  • changed requirements;
  • instructions that no longer apply.

A fresh prompt might be:

Project:
Personal Reading List Tracker using plain HTML, CSS, and JavaScript.

Current state:
- The working tree is clean.
- Add, validate, render, and remove behaviors are working.
- The filter control already exists in index.html.
- Status values are want-to-read, reading, and finished.

Task:
Implement status filtering in app.js.

Expected behavior:
- All shows every book.
- Each status filter shows only matching books.
- Filtering must not modify the books array.
- The active filter remains effective after a book is added or removed.

Constraints:
- Modify only app.js.
- Do not add dependencies.
- Do not change the data model.
- Do not rename status values.
- Do not refactor unrelated functions.

Process:
1. Inspect app.js and PROJECT_BRIEF.md.
2. Do not edit yet.
3. Show the smallest implementation plan.
4. Identify the exact functions that need changes.

This prompt provides current facts without carrying the entire failed conversation forward.

Ask for a Plan That Names Files and Functions

Before approving new edits, require a concrete plan.

A useful plan should say something like:

1. Add an activeFilter variable in app.js.
2. Add getVisibleBooks() to return filtered data without modifying books.
3. Update renderBooks() to use getVisibleBooks().
4. Add a change listener to the existing filter control.
5. Test all filter states after add and remove actions.

A weak plan might say:

I will improve the filtering architecture and make the app scalable.

The first plan is reviewable.

The second leaves the scope undefined.

Use a Session Boundary Checklist

Before starting a new AI coding session, record:

Current branch:
main

Working tree:
clean

Latest verified commit:
2ac7e31 Add book removal

Current task:
Add status filtering

Allowed files:
app.js

Required behavior:
All, Want to Read, Reading, Finished

Excluded:
Dependencies, data-model changes, styling, editing

Verification:
Manual filter tests plus existing add and remove tests

This note protects the session from drifting away from the project state.

It also gives you a clear point for deciding when the task is complete.

Limit the Number of Corrections in One Session

There is no universal maximum number of prompts.

However, repeated corrections are a warning.

A typical pattern may be:

Prompt 1:
Implement the feature.

Prompt 2:
Correct one missed requirement.

Prompt 3:
Fix a specific verified bug.

That can be reasonable.

A more concerning pattern is:

Prompt 1:
Build the feature.

Prompt 2:
Undo part of it.

Prompt 3:
Restore the old data model.

Prompt 4:
Fix the new error.

Prompt 5:
Remove the package.

Prompt 6:
Recreate the deleted function.

Prompt 7:
Make everything work again.

At that point, stop and inspect Git.

The issue is no longer one missing requirement. The session has lost a stable direction.

Avoid Emotional Prompt Escalation

When a tool repeatedly fails, users often write prompts such as:

You broke it again. Fix everything now and do not make any mistakes.

The frustration is understandable, but the prompt does not provide better technical evidence.

A more useful response is:

Stop editing. The current result does not match the task. List the files changed and explain which change implements each acceptance criterion. Do not propose or apply a fix.

Then verify the response with Git.

Precise boundaries are more useful than stronger wording.

Protect Secrets and Private Data During Recovery

A drifting session may create logs, environment files, or copied configuration.

Check for:

.env
.env.local
debug.log
request.json
config.backup

Inspect files carefully without exposing their contents in a public prompt.

Do not paste:

  • API keys;
  • passwords;
  • access tokens;
  • private URLs;
  • company source code;
  • customer information.

If a secret was committed, deleting the line in a later commit may not remove it from existing history or external systems. The credential should be treated as exposed and rotated through the relevant provider.

For the reading list project, the initial version should not require secrets at all.

Prevent Future Sessions from Going Off Track

Recovery is useful, but prevention is better.

Start from a Clean State

Run:

git status

before each feature.

Keep One Task per Session

Do not combine filtering, editing, persistence, and deployment.

Name Allowed Files

For example:

Modify only app.js.

Name Prohibited Changes

For example:

Do not add packages, rename status values, or change the data model.

Ask for Inspection Before Editing

Require the AI to identify the relevant files and functions first.

Review Immediately After One Change

Run:

git status
git diff

before sending the next implementation prompt.

Commit Verified Features

A clean checkpoint makes recovery much easier.

Start a Fresh Session When Context Changes

Do not keep an old conversation merely because it contains many messages.

Current, accurate context is more valuable than long context.

A Recovery Decision Framework

When a session goes off track, choose among three actions.

Continue

Continue when:

  • the diff is still focused;
  • the issue is clearly understood;
  • only one acceptance criterion is missing;
  • the next correction is small;
  • you still understand the project state.

Preserve Part and Restore Part

Use partial recovery when:

  • some changes are correct;
  • useful and unwanted edits can be separated;
  • the valid portion can be tested independently;
  • the remaining diff can become one focused commit.

Discard and Restart

Discard the session when:

  • the architecture changed unexpectedly;
  • dependencies were added without approval;
  • the AI mixed several features;
  • the diff is difficult to explain;
  • the project no longer runs reliably;
  • recovery costs more than rebuilding the feature.

This can be summarized as:

Understandable and focused
→ Continue

Mixed but separable
→ Preserve and restore

Unclear and interconnected
→ Discard and restart

Practical Advice from AI-Assisted Development

Long coding sessions often create the illusion of progress because many files are changing.

File activity is not the same as verified progress.

A useful development state has three qualities:

  • the current behavior is known;
  • the current diff is understood;
  • the next task is limited.

When one of those qualities disappears, pause.

In my experience reviewing AI-assisted changes, the costliest mistakes often happen after the first warning sign. The developer notices that the scope expanded but continues because the AI seems close to finishing.

A better habit is to stop early.

Git makes stopping practical because verified work can be preserved while untrusted work is rejected.

The goal is not to save every generated line.

The goal is to protect the project.

A Reusable Session-Recovery Checklist

Stop

  • Do not approve more edits.
  • Do not request a broad fix.
  • Ask the AI to summarize without changing files.

Inspect

  • Run git status.
  • Run git diff --stat.
  • Run git diff.
  • Inspect untracked files.
  • Check dependency and configuration changes.

Compare

  • Reopen the original prompt.
  • Reopen the project brief.
  • Identify allowed files.
  • Identify excluded features.
  • Match each change to a requirement.

Recover

  • Find the last verified commit.
  • Save a temporary patch when useful.
  • Stage trusted sections selectively.
  • Restore unwanted tracked changes.
  • remove untracked files only after inspection.

Verify

  • Test the recovered feature.
  • Test existing related behavior.
  • Review the remaining diff.
  • Check for secrets and debugging leftovers.

Commit or Restart

  • Commit the verified partial result, or
  • discard the session and return to the last checkpoint.

Begin Again

  • Start a fresh AI session.
  • State the current project state.
  • Define one task.
  • Set file boundaries.
  • Ask for a plan before editing.

Conclusion

An AI coding session has gone off track when the original task, conversation, and repository no longer align.

The safest response is to stop generating changes and inspect the actual project state. Use Git to identify modified and untracked files, compare the diff with the original requirement, find the last verified checkpoint, preserve only the work you understand, and restore everything else.

Do not measure success by how much generated code you manage to save.

A smaller verified change is more valuable than a large result built on unclear assumptions.

The next article will focus on Git checkpoints during AI-assisted development. We will examine where to create checkpoints, how often to commit, how to separate experimental work, and how a clear history makes both recovery and review easier.

Frequently Asked Questions

Should I always discard an AI session after it makes one unrelated change?

No.

Inspect the change first. A small accidental edit may be easy to restore while preserving the valid work. Stop the session when unrelated changes continue or the overall state becomes unclear.

Can I keep only part of an AI-generated file?

Yes.

You can manually preserve a small verified section or use Git’s interactive staging and restore features. Review the result carefully because partial changes may depend on code you removed.

Is starting a fresh AI conversation better than correcting the old one?

Often, yes.

A fresh conversation is useful when the old session contains outdated assumptions, failed approaches, or references to code that no longer exists. Provide the current repository state and one focused task.

Comments

Popular posts from this blog

Claude Code Multi-Agent Setup for Beginners: Models, Roles, and Your First Project

The Limits and Future of Vibe Coding

How to Test and Deploy Your First Vibe-Coded App