Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
02cb371
chore: regenerate pnpm-lock.yaml
Jan 10, 2026
a0cc448
chore: add stub-guard (contentStatus, validator, CI); mark 01-brake-s…
Jan 10, 2026
cff97cc
Docs: formalize v1 content standards and pipeline-aligned Ollama guid…
Jan 11, 2026
071c1af
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
2657155
Update scripts/validate_no_stubs.py
73junito Jan 11, 2026
9fe9bca
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
ba5898e
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
e0ef1cf
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
8aafe33
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
11baa20
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
cc4f5ad
Update .github/workflows/block-stubs.yml
73junito Jan 11, 2026
29f9cb5
Update README.md
73junito Jan 11, 2026
b27ce05
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
0107c0e
Update .github/workflows/block-stubs.yml
73junito Jan 11, 2026
998cc21
docs(bench): add Ollama client benchmark summary and link
Jan 11, 2026
e8410e5
orchestrator: add orchestration skeleton, tests, and CI workflow
Jan 11, 2026
7328ec1
orchestrator: add simple quality guards (min tokens/chars + JSON reje…
Jan 11, 2026
6e82393
Potential fix for code scanning alert no. 44: Workflow does not conta…
73junito Jan 11, 2026
fe17f4d
Update .github/workflows/orchestrate-test.yml
73junito Jan 11, 2026
1768117
Update scripts/orchestrate_content.py
73junito Jan 11, 2026
4e7d435
Update docs/ai/ai-toolkit-guidance.md
73junito Jan 11, 2026
faf1441
Initial plan
Copilot Jan 11, 2026
7550fe9
Add comprehensive test coverage for orchestrate_content.py
Copilot Jan 11, 2026
3dc7236
Initial plan
Copilot Jan 11, 2026
cece8cd
Add HTTP status code check to call_ollama function
Copilot Jan 11, 2026
e5ed3c8
Initial plan
Copilot Jan 11, 2026
f96806a
Initial plan
Copilot Jan 11, 2026
e536322
Fix GitHub API usage in block-stubs workflow
Copilot Jan 11, 2026
1e9c290
Initial plan
Copilot Jan 11, 2026
1590666
Initial plan
Copilot Jan 11, 2026
199617e
Add --dry-run flag support to orchestrate_content.py main()
Copilot Jan 11, 2026
60abc10
Initial plan
Copilot Jan 11, 2026
a5e16a1
Remove unused asyncio import from test_orchestrate_content.py
Copilot Jan 11, 2026
f97fe9b
Resolve conflicts: update orchestrator script and CI workflow
Jan 11, 2026
545b91d
Potential fix for code scanning alert no. 45: Workflow does not conta…
73junito Jan 12, 2026
fb17539
CI: make PR-comment step non-fatal (avoid 403 on forked runs)
Jan 12, 2026
7de5020
validator: scope validate_no_stubs.py to content/ paths only; update …
Jan 12, 2026
bc9bf28
chore: set workspace npm.packageManager to npm
Jan 12, 2026
83a1790
fix(ui): remove duplicated blocks in stories causing build parse errors
Jan 13, 2026
f38f4c6
Merge PR #158: scope validate_no_stubs to content/
Jan 16, 2026
7e6089f
Initial plan
Copilot Jan 12, 2026
d7498bb
Fix esbuild vulnerability by adding override for esbuild ^0.25.0
Copilot Jan 12, 2026
fa97348
Add comprehensive fix summary documentation
Copilot Jan 12, 2026
9add3c5
Update package.json
73junito Jan 17, 2026
3c4252a
Resolve package-lock.json conflicts (keep ours)
Jan 19, 2026
6c4970d
Update documentation to reflect esbuild ^0.27.2 override
Copilot Jan 17, 2026
a35f22b
Merge branch 'main' of https://github.com/73junito/autolearnpro
Jan 28, 2026
e6135dd
Merge branch 'copilot/upgrade-storybook-and-vite'
Jan 28, 2026
fa50215
ci: add pnpm retry loop for Storybook workflow
Jan 28, 2026
251e807
Potential fix for code scanning alert no. 46: Workflow does not conta…
73junito Jan 28, 2026
1ef5b26
Update .github/workflows/playwright.yml
73junito Jan 28, 2026
a67d13a
Update .github/workflows/playwright.yml
73junito Jan 28, 2026
580835d
Initial plan
Copilot Jan 28, 2026
c22abae
Initial plan
Copilot Jan 28, 2026
c7a16ed
Initial plan
Copilot Jan 28, 2026
7db02f0
Initial plan
Copilot Jan 28, 2026
f19f2fb
Initial plan
Copilot Jan 28, 2026
bbe7522
Update .github/scripts/watch_and_fix.js
73junito Jan 28, 2026
254b8e8
Update .github/scripts/watch_and_fix.cjs
73junito Jan 28, 2026
52b9bc8
Initial plan
Copilot Jan 28, 2026
be946d4
Fix orchestrate_content.py: restore missing process_module function
Copilot Jan 28, 2026
0b82e8e
Fix orchestrate_content.py structure: restore missing functions
Copilot Jan 28, 2026
ba8736d
Fix critical control flow error in call_ollama function
Copilot Jan 28, 2026
58a2c8d
Clean up trailing whitespace in orchestrate_content.py
Copilot Jan 28, 2026
2ffbbea
Fix syntax error in orchestrate_content.py caused by bad merge
Copilot Jan 28, 2026
9c2b605
Fix Python syntax error in orchestrate_content.py by restoring correc…
Copilot Jan 29, 2026
ffaee8d
Remove duplicated content from ai-toolkit-guidance.md
Copilot Jan 29, 2026
870107e
Merge pull request #230 from 73junito/copilot/sub-pr-224-please-work
73junito Jan 29, 2026
7efe430
Merge pull request #229 from 73junito/copilot/sub-pr-224-one-more-time
73junito Jan 29, 2026
45623e7
Merge branch 'chore/storybook-pnpm-retry' into copilot/sub-pr-224-yet…
73junito Jan 29, 2026
80c9bb7
Merge pull request #228 from 73junito/copilot/sub-pr-224-yet-again
73junito Jan 29, 2026
dde7fb0
Merge pull request #226 from 73junito/copilot/sub-pr-224-again
73junito Jan 29, 2026
cd271e7
Merge pull request #227 from 73junito/copilot/sub-pr-224-another-one
73junito Jan 29, 2026
c8aa106
Merge branch 'chore/storybook-pnpm-retry' into copilot/sub-pr-224
73junito Jan 29, 2026
b244bfb
Merge pull request #225 from 73junito/copilot/sub-pr-224
73junito Jan 29, 2026
b182dac
chore: keep pyright typeCheckingMode=basic; normalize VSCode settings
Jan 29, 2026
33d862f
chore(lint): apply ruff autofixes (quotes, unused imports, simple for…
Jan 29, 2026
a28bfe0
chore(lint): apply remaining ruff autofixes
Jan 29, 2026
bc76ee8
chore(lint): relax ruff rules for scripts (E501, C901)
Jan 29, 2026
56c3aa7
chore(lint): scope ruff to core Python dirs (include/exclude)
Jan 29, 2026
b55b4dd
chore(lint): scope ruff include to Python files
Jan 29, 2026
12d7ddb
chore(ci): fix test collection and add test deps
Jan 29, 2026
d7f288f
fix(scripts): make generate_thumbnails import-safe (move future impor…
Jan 29, 2026
77d4594
ci: add pytest-httpserver and pytest-asyncio to test deps
Jan 29, 2026
62a7649
ci(tests): remove 'ollama' install; add placeholder course pages gene…
Jan 29, 2026
4ee3a77
ci(tests): install pydantic and local lib/ollama-python in CI
Jan 29, 2026
834002d
ci(tests): make placeholders produce expected course pages and valida…
Jan 29, 2026
b414bf8
ci(python): install aiohttp/httpx/pytest deps in Python CI
Jan 29, 2026
fbc60a0
ci(python): install test deps and local lib/ollama-python editable
Jan 29, 2026
ea8094f
ci(python): checkout submodules for editable install
Jan 29, 2026
8175a15
ci: ensure pnpm install before Playwright tests (fix missing @playwri…
Jan 29, 2026
3272f46
ci: trigger Playwright workflow (noop)
Jan 29, 2026
a4dc0c1
ci(playwright): trigger CI with Node 20 noop
Jan 29, 2026
c3fb050
ci(workflows): enforce Node 20, pnpm install; verify editable ollama …
Jan 29, 2026
39451fe
ci(storybook): use local Storybook CLI in catalog-root script
Jan 29, 2026
c2fedf4
ci(python): install test deps and editable ollama-python in CI
Jan 29, 2026
12748b4
fix(security): avoid rendering raw error messages in UI; log errors s…
Jan 29, 2026
791b99f
chore(security): add ESLint rule to forbid err.message/error.message …
Jan 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 33 additions & 0 deletions .github/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Watch and Fix automation

This folder contains a small script to watch a workflow's recent runs and automatically create
a retry-based fix PR if a failing run is detected.

Usage:

```bash
# PowerShell (temporary env):
`$env:GITHUB_TOKEN='...'; node .github/scripts/watch_and_fix.cjs --owner OWNER --repo REPO [--minutes 60] [--poll 60]; Remove-Item Env:GITHUB_TOKEN`

# Or set for session then run:
`$env:GITHUB_TOKEN='...'
node .github/scripts/watch_and_fix.cjs --owner OWNER --repo REPO [--minutes 60] [--poll 60]
Remove-Item Env:GITHUB_TOKEN`

# cmd.exe one-liner:
`set "GITHUB_TOKEN=..." && node .github/scripts/watch_and_fix.cjs --owner OWNER --repo REPO [--minutes 60] [--poll 60]`

# Recommended: use the `gh` CLI wrapper which authenticates securely and runs the watcher without exposing the PAT:
```powershell
# Authenticate (interactive)
gh auth login

# Then run the wrapper (uses gh auth token under the hood)
powershell -File .github/scripts/run_watcher.ps1 -Owner 73junito -Repo autolearnpro -Minutes 60 -Poll 60
```

Notes:
- Script requires Node 18+ (global `fetch`).
- The script creates a branch and PR that adds a retry loop around `pnpm install` in
`.github/workflows/storybook.yml` to mitigate transient install failures.
Use the `.cjs` script when the repo has `"type": "module"` in `package.json`.
36 changes: 36 additions & 0 deletions .github/scripts/run_watcher.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# PowerShell wrapper: ensures `gh` auth and runs the watcher securely.
param(
[string]$Owner = '73junito',
[string]$Repo = 'autolearnpro',
[int]$Minutes = 60,
[int]$Poll = 60
)

# Check gh
if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
Write-Host "gh CLI not found. Install from https://cli.github.com/ or use winget: winget install --id GitHub.cli -e --source winget"
exit 1
}

# Ensure gh is authenticated
$auth = gh auth status 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "Not authenticated. Running 'gh auth login'..."
gh auth login
if ($LASTEXITCODE -ne 0) { Write-Error "gh auth login failed"; exit 1 }
}

# Get token from gh
$token = gh auth token 2>&1
if ($LASTEXITCODE -ne 0 -or -not $token) {
Write-Error "Failed to obtain token from 'gh auth token'"
exit 1
}

# Run watcher with token in env (session only)
$env:GITHUB_TOKEN = $token
try {
node .github/scripts/watch_and_fix.cjs --owner $Owner --repo $Repo --minutes $Minutes --poll $Poll
} finally {
Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue
}
142 changes: 142 additions & 0 deletions .github/scripts/watch_and_fix.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env node
// CommonJS version of watch_and_fix for repositories with "type": "module".
const { argv, env } = require('process');
const fetch = globalThis.fetch;
if (!fetch) {
console.error('Node runtime must support global fetch (Node 18+).');
process.exit(1);
}

function arg(name) {
const idx = argv.indexOf(`--${name}`);
return idx >= 0 ? argv[idx + 1] : undefined;
}

const OWNER = arg('owner');
const REPO = arg('repo');
const WORKFLOW = arg('workflow') || 'storybook.yml';
const DURATION_MIN = Number(arg('minutes') || '60');
const POLL_SEC = Number(arg('poll') || '60');
let TOKEN = env.GITHUB_TOKEN;
// If no env var provided, try to use `gh auth token` if available to avoid prompting for PAT
if (!TOKEN) {
try {
const { execSync } = require('child_process');
const out = execSync('gh auth token', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
if (out) {
TOKEN = out;
console.log('Using token from `gh auth token`.');
}
} catch (e) {
// ignore - will error later if TOKEN still unset
}
}

if (!OWNER || !REPO) {
console.error('Usage: node watch_and_fix.cjs --owner OWNER --repo REPO [--minutes 60] [--poll 60]');
process.exit(2);
}

if (!TOKEN) {
console.error('No GitHub token available. Set GITHUB_TOKEN or run `gh auth login` and ensure `gh auth token` returns a token.');
process.exit(2);
}

const API = `https://api.github.com/repos/${OWNER}/${REPO}`;

async function gh(path, opts = {}) {
opts.headers = Object.assign({}, opts.headers || {}, {
Authorization: `token ${TOKEN}`,
'User-Agent': 'watch-and-fix-script',
Accept: 'application/vnd.github+json',
});
const res = await fetch(`${API}${path}`, opts);
if (!res.ok) {
const text = await res.text();
if (res.status === 401) {
throw new Error('Unauthorized (401): GitHub token invalid or missing required scopes.\n' +
'When running locally, create a Personal Access Token with `repo` scope and set it as GITHUB_TOKEN.\n' +
'See: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token');
}
throw new Error(`${res.status} ${res.statusText} ${text}`);
}
return res.json();
}

async function getLatestRun() {
const path = `/actions/workflows/${WORKFLOW}/runs?per_page=1`;
const data = await gh(path);
return data.workflow_runs && data.workflow_runs[0];
}

async function createRetryFixPR(defaultBranch) {
const path = `/contents/.github/workflows/storybook.yml`;
const file = await gh(path + `?ref=${defaultBranch}`);
const content = Buffer.from(file.content, 'base64').toString('utf8');

const installBlockRegex = /- name: Install dependencies \(ui\)[\s\S]*?- name:/m;
const retryBlock = `- name: Install dependencies (ui)\n working-directory: packages/ui\n run: |\n set -euo pipefail\n echo "Installing dependencies with retries"\n for i in 1 2 3; do\n pnpm install --frozen-lockfile && break || { echo "pnpm install failed (attempt $i)"; sleep $((i * 5)); }\n done\n\n - name:`;

if (!installBlockRegex.test(content)) {
console.error('Could not find install block to replace. Aborting automated fix.');
return null;
}

const newContent = content.replace(installBlockRegex, retryBlock);

// create branch
const timestamp = Date.now();
const branch = `autofix/storybook-retry-${timestamp}`;
const defaultRef = await gh(`/git/ref/heads/${defaultBranch}`);
const sha = defaultRef.object.sha;
await gh('/git/refs', { method: 'POST', body: JSON.stringify({ ref: `refs/heads/${branch}`, sha }) });

// update file on new branch
const putPath = `/contents/.github/workflows/storybook.yml`;
const body = {
message: 'chore: retry pnpm install in Storybook workflow (automated)',
content: Buffer.from(newContent, 'utf8').toString('base64'),
branch,
sha: file.sha,
};
await gh(putPath, { method: 'PUT', body: JSON.stringify(body) });

// create PR
const pr = await gh('/pulls', {
method: 'POST',
body: JSON.stringify({ title: 'Automated: retry pnpm install for Storybook build', head: branch, base: defaultBranch, body: 'Automated fix: add retries to pnpm install to mitigate transient failures.' }),
});
return pr.html_url;
}

async function main() {
const repoInfo = await gh('');
const defaultBranch = repoInfo.default_branch || 'main';

const end = Date.now() + DURATION_MIN * 60 * 1000;
console.log(`Watching ${OWNER}/${REPO} workflow ${WORKFLOW} for ${DURATION_MIN} minutes (poll ${POLL_SEC}s)`);

let lastSeenRunId = null;
while (Date.now() < end) {
try {
const run = await getLatestRun();
if (run && run.id !== lastSeenRunId) {
lastSeenRunId = run.id;
console.log(`Latest run #${run.id} - status=${run.status} conclusion=${run.conclusion} url=${run.html_url}`);
if (run.conclusion && run.conclusion !== 'success') {
console.log('Detected failing run — attempting automated fix');
const prUrl = await createRetryFixPR(defaultBranch);
if (prUrl) console.log('Created PR:', prUrl);
else console.log('Automated fix not applied.');
return;
}
}
} catch (err) {
console.error('Error while checking runs:', err.message || err);
}
await new Promise(r => setTimeout(r, POLL_SEC * 1000));
}
console.log('Watch duration completed with no failing run detected.');
}

main().catch(err => { console.error(err); process.exit(1); });
118 changes: 118 additions & 0 deletions .github/scripts/watch_and_fix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env node
// Watch Storybook workflow runs for failures and create an automated retry-fix PR.
// Usage: GITHUB_TOKEN=... node .github/scripts/watch_and_fix.js --owner OWNER --repo REPO

const { argv, env } = require('process');
const fetch = globalThis.fetch;
if (!fetch) {
console.error('Node runtime must support global fetch (Node 18+).');
process.exit(1);
}

function arg(name) {
const idx = argv.indexOf(`--${name}`);
return idx >= 0 ? argv[idx + 1] : undefined;
}

const OWNER = arg('owner');
const REPO = arg('repo');
const WORKFLOW = arg('workflow') || 'storybook.yml';
const DURATION_MIN = Number(arg('minutes') || '60');
const POLL_SEC = Number(arg('poll') || '60');
const TOKEN = env.GITHUB_TOKEN;

if (!OWNER || !REPO || !TOKEN) {
console.error('Usage: GITHUB_TOKEN=... node watch_and_fix.js --owner OWNER --repo REPO [--minutes 60] [--poll 60]');
process.exit(2);
}

const API = `https://api.github.com/repos/${OWNER}/${REPO}`;

async function gh(path, opts = {}) {
opts.headers = Object.assign({}, opts.headers || {}, {
Authorization: `token ${TOKEN}`,
'User-Agent': 'watch-and-fix-script',
Accept: 'application/vnd.github+json',
});
const res = await fetch(`${API}${path}`, opts);
if (!res.ok) throw new Error(`${res.status} ${res.statusText} ${await res.text()}`);
return res.json();
}

async function getLatestRun() {
const path = `/actions/workflows/${WORKFLOW}/runs?per_page=1`;
const data = await gh(path);
return data.workflow_runs && data.workflow_runs[0];
}

async function createRetryFixPR(defaultBranch) {
const path = `/contents/.github/workflows/storybook.yml`;
const file = await gh(path + `?ref=${defaultBranch}`);
const content = Buffer.from(file.content, 'base64').toString('utf8');

const installBlockRegex = /- name: Install dependencies \(ui\)[\s\S]*?- name:/m;
const retryBlock = `- name: Install dependencies (ui)\n working-directory: packages/ui\n run: |\n set -euo pipefail\n echo "Installing dependencies with retries"\n for i in 1 2 3; do\n pnpm install --frozen-lockfile && break || { echo "pnpm install failed (attempt $i)"; sleep $((i * 5)); }\n done\n\n - name:`;

if (!installBlockRegex.test(content)) {
console.error('Could not find install block to replace. Aborting automated fix.');
return null;
}

const newContent = content.replace(installBlockRegex, retryBlock);

// create branch
const timestamp = Date.now();
const branch = `autofix/storybook-retry-${timestamp}`;
const defaultRef = await gh(`/git/ref/heads/${defaultBranch}`);
const sha = defaultRef.object.sha;
await gh('/git/refs', { method: 'POST', body: JSON.stringify({ ref: `refs/heads/${branch}`, sha }) });

// update file on new branch
const putPath = `/contents/.github/workflows/storybook.yml`;
const body = {
message: 'chore: retry pnpm install in Storybook workflow (automated)',
content: Buffer.from(newContent, 'utf8').toString('base64'),
branch,
sha: file.sha,
};
await gh(putPath, { method: 'PUT', body: JSON.stringify(body) });

// create PR
const pr = await gh('/pulls', {
method: 'POST',
body: JSON.stringify({ title: 'Automated: retry pnpm install for Storybook build', head: branch, base: defaultBranch, body: 'Automated fix: add retries to pnpm install to mitigate transient failures.' }),
});
return pr.html_url;
}

async function main() {
const repoInfo = await gh('');
const defaultBranch = repoInfo.default_branch || 'main';

const end = Date.now() + DURATION_MIN * 60 * 1000;
console.log(`Watching ${OWNER}/${REPO} workflow ${WORKFLOW} for ${DURATION_MIN} minutes (poll ${POLL_SEC}s)`);

let lastSeenRunId = null;
while (Date.now() < end) {
try {
const run = await getLatestRun();
if (run && run.id !== lastSeenRunId) {
lastSeenRunId = run.id;
console.log(`Latest run #${run.id} - status=${run.status} conclusion=${run.conclusion} url=${run.html_url}`);
if (run.conclusion && run.conclusion !== 'success') {
console.log('Detected failing run — attempting automated fix');
const prUrl = await createRetryFixPR(defaultBranch);
if (prUrl) console.log('Created PR:', prUrl);
else console.log('Automated fix not applied.');
return;
}
}
} catch (err) {
console.error('Error while checking runs:', err.message || err);
}
await new Promise(r => setTimeout(r, POLL_SEC * 1000));
}
console.log('Watch duration completed with no failing run detected.');
}

main().catch(err => { console.error(err); process.exit(1); });
Loading
Loading