Install
$ agentstack add skill-code-saurabh-openskills-agent-memory ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Agent Memory — Unified Skill
This skill unifies three complementary capabilities into a single, coherent workflow:
/learn— Session Memory Management — Persist, search, prune, and export what the agent has learned about your codebase and project across sessions./browse— Browser-Based QA Testing — Drive real Chromium (headless or headed), click elements, take screenshots, import cookies, and assert page state for production-grade QA./pair-agent— Multi-Agent Coordination — Spawn parallel sub-agents on isolated scopes, aggregate results, and hand off context cleanly between agents.
Part 1 — Memory Management (/learn)
1.1 Philosophy: Why Memory Matters
Every time the agent explores your codebase, it discovers implicit patterns that are expensive to rediscover:
- Which modules are load-bearing vs. experimental
- Naming conventions that are project-local (not language-default)
- Fragile integration points that have broken before
- Performance invariants the team cares about
- Domain-specific jargon used in comments and variable names
Without memory, the agent starts cold every session. With memory, compound knowledge accumulates — each session builds on prior sessions, reducing repeated archaeology and increasing the precision of every suggestion.
Principle: Memory is a first-class engineering artifact. Treat it like documentation.
1.2 Memory Scopes
There are two distinct scopes of memory. Always be explicit about which scope you are reading from or writing to.
Global Memory (~/.agent-memory/global/)
Stores patterns that apply across all projects on this machine:
- Preferred code style decisions (e.g., "always use early-return guards")
- Recurring architectural preferences (e.g., "prefer repository pattern over active record")
- User-level shortcuts and workflow preferences
- Learned tool behaviors (e.g., "this user's Makefile uses
make lintnotmake check")
Project Memory (/.agent-memory/)
Stores patterns specific to the current repository:
- Module ownership and dependency graph insights
- Known flaky test files and their workarounds
- Environment setup quirks (e.g., "must source
.env.localbefore running tests") - API contracts with external services this codebase integrates with
- Domain concepts: entity names, lifecycle states, event vocabulary
- Historical bugs and their root causes (for future regression awareness)
- Architecture Decision Records (ADRs) that affect code shape
Rule: If a pattern applies only to one repo, it MUST be written to project memory, not global. Polluting global memory with project-specific facts degrades the signal-to-noise ratio for all projects.
1.3 Memory File Format
All memory entries are stored as structured Markdown files for human readability and diff-friendliness.
.agent-memory/
├── patterns/
│ ├── naming-conventions.md
│ ├── architecture.md
│ ├── testing-patterns.md
│ └── fragile-zones.md
├── domains/
│ ├── entities.md
│ └── events.md
├── history/
│ ├── bugs-resolved.md
│ └── refactors-completed.md
└── index.md ← master index with dates and tags
Each entry in a memory file follows this schema:
## [PATTERN_ID] Short Title
**Learned:** YYYY-MM-DD
**Confidence:** high | medium | low
**Scope:** global | project
**Tags:** #architecture #naming #performance
**Source:** session-id or description of how this was discovered
### Observation
What was observed.
### Pattern
The generalizable rule extracted from the observation.
### Example
Concrete code or command example.
### Caveats
Conditions under which this pattern does NOT apply.
1.4 Writing New Memory
When you discover something worth remembering, write it immediately — not at the end of the session, because sessions can be interrupted.
Trigger conditions for writing a new memory entry:
- You found a non-obvious architectural invariant (e.g., "all writes go through the event bus, never direct DB calls")
- You discovered a project-specific naming rule not in any README
- You debugged a recurring error class and want to short-circuit future debugging
- You learned how the build system behaves in an undocumented edge case
- The user explicitly says "remember this" or "add this to memory"
Writing procedure:
# 1. Determine scope
SCOPE="project" # or "global"
# 2. Determine the correct category file
CATEGORY="patterns/architecture" # or naming-conventions, testing-patterns, etc.
# 3. Generate a unique pattern ID
PATTERN_ID="ARCH-$(date +%Y%m%d)-001"
# 4. Write the entry (append to the category file)
# Use the schema defined in section 1.3 above.
# 5. Update the index
echo "- [$PATTERN_ID] Short title — $CATEGORY — $(date +%Y-%m-%d)" >> .agent-memory/index.md
1.5 Reading and Reviewing Memory
At the START of every session on a known project, load the memory index and the relevant category files before making any code suggestions.
# Load the project memory index
cat .agent-memory/index.md 2>/dev/null || echo "No project memory yet."
# Load global memory index
cat ~/.agent-memory/global/index.md 2>/dev/null || echo "No global memory yet."
When the user asks to review memory:
- Print the full index with IDs, titles, dates, and tags.
- Group entries by category (patterns, domains, history).
- Flag entries older than 90 days as "⚠️ Stale — review for pruning."
- Flag entries with
Confidence: lowas "🔍 Uncertain — verify before relying on."
1.6 Searching Memory
When the user asks to search memory for a topic:
# Search all memory files for a keyword
grep -r "KEYWORD" .agent-memory/ --include="*.md" -l
# Search with context lines
grep -r "KEYWORD" .agent-memory/ --include="*.md" -n -B 2 -A 4
Search is case-insensitive by default. Present results grouped by file, with the pattern ID and title prominently displayed.
1.7 Pruning Memory
Memory must be pruned to stay useful. Stale or wrong memory is worse than no memory — it misleads future sessions.
Prune when:
- A refactor changed the architectural pattern the entry described
- A dependency was removed (its patterns are now irrelevant)
- An entry is >180 days old and hasn't been referenced or validated
- The user explicitly says "this is no longer true"
- The confidence level was
lowand it never got validated
Prune procedure:
# 1. List entries older than 90 days
find .agent-memory/ -name "*.md" -mtime +90
# 2. For each candidate, present to user for confirm/keep/update
# 3. Delete confirmed-stale entries
# 4. Remove from index.md
# 5. Commit the pruning as a standalone commit with message:
# "chore(memory): prune stale entries — YYYY-MM-DD"
Never silently delete memory. Always show the user what will be removed and get confirmation (or use --force flag only if user explicitly requests unattended pruning).
1.8 Exporting Memory
When the user wants to export memory (e.g., to share with a new team member, or to seed a new project):
# Export as a single consolidated Markdown document
cat .agent-memory/**/*.md > memory-export-$(date +%Y%m%d).md
# Export as JSON for programmatic use
python3 -c "
import os, json, re
from pathlib import Path
entries = []
for f in Path('.agent-memory').rglob('*.md'):
content = f.read_text()
entries.append({'file': str(f), 'content': content})
print(json.dumps(entries, indent=2))
" > memory-export-$(date +%Y%m%d).json
1.9 How Learnings Compound Across Sessions
The compounding effect works as follows:
| Session | What Happens | |---------|--------------| | 1 | Agent discovers project structure, writes 3–5 foundational entries | | 2 | Agent loads index, skips re-exploration, writes 2–3 deeper entries | | 3 | Agent cross-references existing patterns, catches inconsistencies | | 5+ | Agent predicts issues before the user encounters them | | 10+ | Agent functions like a senior team member who knows the codebase |
Key behaviors that enable compounding:
- Always load memory at session start (not on demand)
- Cross-reference new discoveries against existing entries before writing duplicates
- Upgrade confidence levels when an entry is validated a second time
- Link related entries using
[PATTERN_ID]references within entries
Part 2 — Browser QA Testing (/browse)
2.1 Philosophy
Real QA requires a real browser. Mocks and unit tests cannot catch:
- Layout regressions caused by CSS specificity conflicts
- Race conditions in JavaScript initialization
- Server-side rendering hydration mismatches
- Authentication flows that depend on session cookies
- Third-party widget interactions (analytics, chat widgets, payment iframes)
This skill drives real Chromium via Playwright, giving you pixel-accurate, JS-executing, network-real browser automation.
2.2 Prerequisites and Setup
# Install Playwright with Chromium
pip install playwright
playwright install chromium
# Or via npm
npm install -D playwright
npx playwright install chromium
# Verify installation
python3 -c "from playwright.sync_api import sync_playwright; print('Playwright OK')"
2.3 Basic Navigation and Screenshot
from playwright.sync_api import sync_playwright
import os
def browse_and_screenshot(url: str, output_path: str = "screenshot.png"):
"""
Navigate to a URL and capture a full-page screenshot.
Uses stealth settings to avoid bot detection.
"""
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
args=[
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--no-zygote",
"--disable-gpu",
]
)
context = browser.new_context(
viewport={"width": 1440, "height": 900},
user_agent=(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
),
locale="en-US",
timezone_id="America/New_York",
)
# Anti-bot: remove webdriver property
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
});
""")
page = context.new_page()
page.goto(url, wait_until="networkidle", timeout=30000)
page.screenshot(path=output_path, full_page=True)
browser.close()
print(f"Screenshot saved to {output_path}")
browse_and_screenshot("https://localhost:3000", "qa-screenshot.png")
2.4 Real Clicks and Form Interactions
def test_login_flow(base_url: str, email: str, password: str):
"""
Test a real login flow with form filling and navigation assertions.
"""
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(f"{base_url}/login")
# Fill form fields
page.fill('input[name="email"]', email)
page.fill('input[name="password"]', password)
# Screenshot before submit (for debugging)
page.screenshot(path="before-login.png")
# Click submit and wait for navigation
with page.expect_navigation(wait_until="networkidle"):
page.click('button[type="submit"]')
# Assert post-login destination
assert "/dashboard" in page.url, f"Expected redirect to /dashboard, got {page.url}"
# Assert key element is present
page.wait_for_selector('[data-testid="welcome-message"]', timeout=5000)
# Screenshot after login
page.screenshot(path="after-login.png")
browser.close()
print("Login flow: PASS")
2.5 Anti-Bot Stealth Configuration
For sites with advanced bot detection (Cloudflare, Akamai, DataDome):
def stealth_context(playwright_instance):
"""
Create a maximum-stealth browser context.
Mimics a real macOS Chrome user as closely as possible.
"""
browser = playwright_instance.chromium.launch(
headless=True,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-features=IsolateOrigins,site-per-process",
]
)
context = browser.new_context(
viewport={"width": 1440, "height": 900},
screen={"width": 1440, "height": 900},
user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
locale="en-US",
timezone_id="America/Los_Angeles",
color_scheme="light",
device_scale_factor=2.0, # Retina display
has_touch=False,
java_script_enabled=True,
accept_downloads=True,
extra_http_headers={
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
)
# Remove all automation fingerprints
context.add_init_script("""
// Hide webdriver
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
// Fake plugins
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5],
});
// Fake languages
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en'],
});
// Hide Chrome automation
window.chrome = { runtime: {} };
// Fake permissions
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters)
);
""")
return browser, context
2.6 Cookie Import from Real Browser
To test authenticated sessions without re-logging in via automation (which often triggers 2FA):
Step 1: Export cookies from your real browser
Use the browser extension "Cookie-Editor" (Chrome/Firefox) to export cookies as JSON, or use the EditThisCookie extension.
Alternatively, export from Chrome's profile directly:
# macOS — copy Chrome's cookies database (requires Chrome to be closed)
cp ~/Library/Application\ Support/Google/Chrome/Default/Cookies /tmp/chrome-cookies.db
# Extract cookies for a specific domain using sqlite3
sqlite3 /tmp/chrome-cookies.db \
"SELECT host_key, name, value, path, expires_utc, is_secure, is_httponly
FROM cookies WHERE host_key LIKE '%yourdomain.com%';"
Step 2: Load cookies into Playwright context
import json
def load_cookies_from_file(context, cookie_file: str):
"""
Load cookies exported from a real browser into a Playwright context.
Supports the Cookie-Editor JSON export format.
"""
with open(cookie_file) as f:
raw_cookies = json.load(f)
playwright_cookies = []
for c in raw_cookies:
cookie = {
"name": c["name"],
"value": c["value"],
"domain": c.get("domain", ""),
"path": c.get("path", "/"),
"secure": c.get("secure", False),
"httpOnly": c.get("httpOnly", False),
}
# Handle expiry
if "expirationDate" in c:
cookie["expires"] = int(c["expirationDate"])
playwright_cookies.append(cookie)
context.add_cookies(playwright_cookies)
print(f"Loaded {len(playwright_cookies)} cookies.")
# Usage
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
load
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [CODE-SAURABH](https://github.com/CODE-SAURABH)
- **Source:** [CODE-SAURABH/OpenSkills](https://github.com/CODE-SAURABH/OpenSkills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.