Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
{
"permissions": {
"allow": [
"Bash(npx playwright:*)"
"allow": ["Bash(npx playwright:*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/scripts/claude-guard.js\""
}
]
}
]
}
}
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Check formatting
run: npm run format:check

- name: Run unit tests
run: npm test

Expand Down
5 changes: 5 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
npx lint-staged
npm test
if git diff --cached --name-only | grep -qx "manifest.json"; then
node scripts/release-check.js --staged
fi
1 change: 1 addition & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node scripts/pre-push.js
10 changes: 10 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
lib/
node_modules/
playwright-report/
test-results/
coverage/
*.zip
*.min.js
HTMLReport/TableFilter/
package-lock.json
*.min.css
7 changes: 7 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"semi": true,
"printWidth": 100,
"trailingComma": "es5",
"endOfLine": "auto"
}
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Load the extension manually: `chrome://extensions` → Developer mode → **Load
| `npm run test:e2e:ui` / `:debug` | Playwright UI mode / step debugger |
| `npm run test:e2e:crop` | Crop-screenshot specs, headed |
| `npm run test:e2e:report` | Open last HTML report |
| `npm run lint` / `npm run format` | ESLint / Prettier write (`format:check` only verifies) |
| `npm run check` | Lint + format check + unit tests |
| `npm run release:check` | Release gate: version bumped, `package.json` = `manifest.json`, zip allowlist covers the manifest, no high/critical `npm audit` findings |
| `npm run test:all` | Unit + full E2E |
| `.\genetareZip.ps1` | Build `ETExtension-<timestamp>.zip` for Web Store upload (PowerShell, runtime files only) |
| `.\start_test_server.ps1` | `python -m http.server 8000` serving the repo; open `/test/e2e/test-pages/index.html` |
Expand Down Expand Up @@ -160,9 +163,19 @@ If your agent supports these, use them:

## Definition of done

1. `npm test` passes.
1. `npm run check` passes (ESLint, Prettier check, Jest). Run `npm run format` to fix formatting.
2. Relevant E2E specs pass (`npm run test:e2e:all` for UI / message-flow changes).
3. New `chrome.*` calls are mocked in `jest.setup.js`; new specs are wired into `package.json`.
4. Extension reloaded and the change checked manually in Chrome when it affects UI.
5. The tutorial (`js/tutorial.js`, `tutorial.html`, images) and README features / screenshots updated if the change is user-visible.
6. This file updated if architecture, commands or conventions changed.
7. Release: bump `manifest.json` `version` (and `package.json` to match), then `npm run release:check` passes.

### Git hooks (Husky)

Installed by `npm install` (`prepare` script). They are the gate; do not bypass them.

- **pre-commit**: lint-staged (ESLint + Prettier on staged files), full Jest suite, and `release:check` when the content of `manifest.json` changes.
- **pre-push**: headless E2E (`npm run test:e2e:ci`) when runtime files changed in the pushed range; skipped otherwise. CI runs the same suite.
- Never use `--no-verify` or `HUSKY=0`; a Claude Code hook (`scripts/claude-guard.js`) blocks them. If a hook fails, fix the cause.
- Why the checks are split this way: `docs/adr/0001-tiered-git-hooks.md`.
76 changes: 38 additions & 38 deletions HTMLReport/modules/reportData.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,35 @@ import { Bug, Note, Idea, Question } from '../../src/Annotation.js';
* @returns {Promise<Session|null>} The reconstructed session or null if no data.
*/
export async function loadSessionData() {
const response = await chrome.runtime.sendMessage({ type: "getSessionData" });
if (!response || response.annotationsCount === 0) {
return null;
}
const response = await chrome.runtime.sendMessage({ type: 'getSessionData' });
if (!response || response.annotationsCount === 0) {
return null;
}

const sessionData = await chrome.runtime.sendMessage({ type: "getFullSession" });
if (!sessionData) {
throw new Error('Could not get full session data');
}
const sessionData = await chrome.runtime.sendMessage({ type: 'getFullSession' });
if (!sessionData) {
throw new Error('Could not get full session data');
}

const session = new Session(sessionData.startDateTime, sessionData.browserInfo);
const session = new Session(sessionData.startDateTime, sessionData.browserInfo);

const annotationConstructors = { Bug, Note, Idea, Question };
const addMethods = { Bug: 'addBug', Note: 'addNote', Idea: 'addIdea', Question: 'addQuestion' };
const annotationConstructors = { Bug, Note, Idea, Question };
const addMethods = { Bug: 'addBug', Note: 'addNote', Idea: 'addIdea', Question: 'addQuestion' };

sessionData.annotations.forEach(annotation => {
const Constructor = annotationConstructors[annotation.type];
if (Constructor) {
const newAnnotation = new Constructor(
annotation.name, annotation.url, annotation.timestamp, annotation.imageURL
);
session[addMethods[annotation.type]](newAnnotation);
}
});
sessionData.annotations.forEach((annotation) => {
const Constructor = annotationConstructors[annotation.type];
if (Constructor) {
const newAnnotation = new Constructor(
annotation.name,
annotation.url,
annotation.timestamp,
annotation.imageURL
);
session[addMethods[annotation.type]](newAnnotation);
}
});

return session;
return session;
}

/**
Expand All @@ -40,12 +43,9 @@ export async function loadSessionData() {
* @returns {Promise<object>} The response from the background script.
*/
export function deleteAnnotation(annotationID) {
return new Promise((resolve) => {
chrome.runtime.sendMessage(
{ type: "deleteAnnotation", annotationID },
resolve
);
});
return new Promise((resolve) => {
chrome.runtime.sendMessage({ type: 'deleteAnnotation', annotationID }, resolve);
});
}

/**
Expand All @@ -54,15 +54,15 @@ export function deleteAnnotation(annotationID) {
* @returns {object}
*/
export function serializeSession(session) {
return {
startDateTime: session.getStartDateTime(),
browserInfo: session.getBrowserInfo(),
annotations: session.getAnnotations().map(a => ({
type: a.constructor.name,
name: a.name,
url: a.url,
timestamp: a.timestamp,
imageURL: a.imageURL
}))
};
return {
startDateTime: session.getStartDateTime(),
browserInfo: session.getBrowserInfo(),
annotations: session.getAnnotations().map((a) => ({
type: a.constructor.name,
name: a.name,
url: a.url,
timestamp: a.timestamp,
imageURL: a.imageURL,
})),
};
}
Loading
Loading