@double_geste/agent-standup 0.0.0-stage → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arthur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,18 @@
1
- # Temporary Holding Version
1
+ # Agent Standup (command line)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Your Claude Code sessions as a live team call, without any editor. Free, MIT, and it never calls Claude: it only reads the session files Claude Code already writes.
4
+
5
+ ```bash
6
+ npx @double_geste/agent-standup # then open http://localhost:4747 (add --open to open it for you)
7
+ npx @double_geste/agent-standup status # ⚙ 3 working · ✋ 1 needs you
8
+ ```
9
+
10
+ Install once for status bars (faster than npx): `npm i -g @double_geste/agent-standup`
11
+
12
+ - **Claude Code status line**: in `~/.claude/settings.json` add `{ "statusLine": { "type": "command", "command": "agent-standup statusline" } }`
13
+ - **tmux**: `set -g status-right '#(agent-standup status --here)'`
14
+ - **Anything else**: `agent-standup status --json`
15
+
16
+ Full documentation, the VS Code extension, the desktop app and the demo: https://atoll92.github.io/agent-standup/ · https://github.com/Atoll92/agent-standup.git
17
+
18
+ Unofficial: not affiliated with Anthropic.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // Agent Standup from a terminal: the live call in your browser, and one-line status for status bars (Claude Code, tmux, Neovim…).
3
+ // agent-standup [serve] [--port 4747] [--open] start the local server and (optionally) open the page
4
+ // agent-standup status [--here] [--json] counts of working / needs you / your turn
5
+ // agent-standup statusline same, one short line; reads Claude Code's statusLine JSON on stdin
6
+ // Everything reads the local session files Claude Code already writes. Nothing is sent anywhere.
7
+ const path = require('path'), { spawn } = require('child_process');
8
+ const pkg = require('../package.json');
9
+ const argv = process.argv.slice(2), flags = {}, rest = [];
10
+ for (let i = 0; i < argv.length; i++) {
11
+ const a = argv[i], val = () => argv[++i];
12
+ if (a === '--port') flags.port = val(); else if (a === '--projects') flags.projects = val(); else if (a === '--characters') flags.characters = val(); else if (a === '--config-dir') flags.config = val();
13
+ else if (a === '--open') flags.open = true; else if (a === '--here') flags.here = true; else if (a === '--json') flags.json = true; else if (a === '--titles') flags.titles = true;
14
+ else if (a === '-h' || a === '--help') flags.help = true; else if (a === '-v' || a === '--version') flags.version = true; else rest.push(a);
15
+ }
16
+ const cmd = rest[0] || 'serve';
17
+ const HELP = `Agent Standup ${pkg.version} — your Claude Code agents as a live video call
18
+
19
+ Usage
20
+ agent-standup [serve] [--port 4747] [--open] start the local server (http://localhost:4747)
21
+ agent-standup status [--here] [--json] what is going on right now (--here: only the current folder)
22
+ agent-standup statusline one short line for a status bar (reads Claude Code's JSON on stdin)
23
+
24
+ Options
25
+ --port N port of the local server (default 4747)
26
+ --open open the page in your browser
27
+ --projects DIR where the session files are (default ~/.claude/projects, or $CLAUDE_CONFIG_DIR/projects)
28
+ --characters DIR folder with your own pixel-art characters (default ~/.agent-standup/characters)
29
+ --here status: only sessions of the current folder
30
+ --json status: machine-readable output (add --titles to include session titles)
31
+ `;
32
+ if (flags.help || cmd === 'help') { process.stdout.write(HELP); process.exit(0); }
33
+ if (flags.version || cmd === 'version') { console.log(pkg.version); process.exit(0); }
34
+ if (flags.projects) process.env.AGENT_STANDUP_PROJECTS = path.resolve(flags.projects);
35
+ if (flags.config) process.env.CLAUDE_CONFIG_DIR = path.resolve(flags.config);
36
+ if (flags.characters) process.env.AGENT_STANDUP_CHARACTERS = path.resolve(flags.characters);
37
+ if (flags.port) process.env.PORT = String(parseInt(flags.port, 10) || 4747);
38
+ const server = require('../server.js');
39
+ const projKey = (dir) => String(dir || '').replace(/[^a-zA-Z0-9]/g, '-'); // Claude Code's project folder naming
40
+
41
+ function snapshot(dir) {
42
+ const now = Date.now(), all = server.boardRows(), rows = dir ? all.filter((r) => r.proj === projKey(dir)) : all;
43
+ const act = rows.filter((r) => r.state !== 'idle' || now - r.mtime < 15 * 60e3), n = (st) => act.filter((r) => r.state === st).length;
44
+ return { rows: act, needsYou: n('blocked'), working: n('working') + n('waiting'), yourTurn: n('turn'), idle: rows.length - act.length };
45
+ }
46
+ const line = (s, short) => { const p = []; if (s.needsYou) p.push(short ? `✋${s.needsYou}` : `✋ ${s.needsYou} needs you`); if (s.working) p.push(short ? `⚙${s.working}` : `⚙ ${s.working} working`); if (!s.needsYou && !s.working && s.yourTurn) p.push(short ? '✓' : '✓ your turn'); return p.join(short ? ' ' : ' · ') || (short ? '' : 'idle'); };
47
+
48
+ if (cmd === 'status') {
49
+ const s = snapshot(flags.here ? process.cwd() : '');
50
+ if (flags.json) console.log(JSON.stringify({ needsYou: s.needsYou, working: s.working, yourTurn: s.yourTurn, idle: s.idle, sessions: s.rows.map((r) => ({ project: r.proj, id: r.id, state: r.state, tool: r.tool, ageMs: Math.round(r.ageMs), subAgentsActive: r.subActive, subAgents: r.subTotal, ...(flags.titles ? { title: r.title } : {}) })) }, null, 2));
51
+ else console.log(line(s, false));
52
+ } else if (cmd === 'statusline') {
53
+ let raw = ''; const go = () => { let dir = ''; try { const j = JSON.parse(raw); dir = (j.workspace && j.workspace.current_dir) || j.cwd || ''; } catch {} console.log(line(snapshot(dir), true)); process.exit(0); };
54
+ if (process.stdin.isTTY) go(); else { process.stdin.setEncoding('utf8'); process.stdin.on('data', (d) => (raw += d)); process.stdin.on('end', go); setTimeout(go, 800); }
55
+ } else if (cmd === 'serve') {
56
+ const port = +process.env.PORT || 4747, srv = server.start(port);
57
+ srv.on('error', (e) => { console.error(e.code === 'EADDRINUSE' ? `Port ${port} is already in use (Agent Standup may already be running, e.g. inside your editor). Try: agent-standup --port ${port + 1}` : String(e)); process.exit(1); });
58
+ srv.on('listening', () => { if (flags.open) { const url = `http://localhost:${port}/`, opener = process.platform === 'darwin' ? ['open', [url]] : process.platform === 'win32' ? ['cmd', ['/c', 'start', '', url]] : ['xdg-open', [url]]; try { spawn(opener[0], opener[1], { stdio: 'ignore', detached: true }).on('error', () => {}).unref(); } catch {} } });
59
+ process.on('SIGINT', () => { try { server.shutdown(); } catch {} process.exit(0); });
60
+ } else { console.error(`Unknown command "${cmd}".\n\n${HELP}`); process.exit(2); }
package/demo.js ADDED
@@ -0,0 +1,95 @@
1
+ // A made-up session used by the built-in demo (?demo=1, "Try the demo"): a fictional team fixes a slow login page
2
+ // and flaky cart tests. No real project data. Same shape as what server.js builds from real transcripts.
3
+ // `fr` holds the French version of spoken lines, so the demo needs no translation service.
4
+ module.exports = function demoTimeline() {
5
+ const start = Date.now() - 8 * 60 * 1000;
6
+ let now = start, n = 0;
7
+ const events = [];
8
+ const agents = [
9
+ { id: 'main', role: 'Lead', type: 'main' },
10
+ { id: 'ada', name: 'Ada', role: 'Login profiling', roleFr: 'Profilage du login', type: 'general-purpose' },
11
+ { id: 'remy', name: 'Remy', role: 'Flaky cart tests', roleFr: 'Tests du panier instables', type: 'general-purpose' },
12
+ { id: 'iris', name: 'Iris', role: 'API error review', roleFr: 'Revue des erreurs API', type: 'general-purpose' },
13
+ { id: 'hugo', name: 'Lena', role: 'Retry logic check', roleFr: 'Vérif. de la logique de retry', type: 'general-purpose' },
14
+ ];
15
+ const cat = (lines) => lines.map((l, i) => String(i + 1).padStart(6) + '\t' + l).join('\n');
16
+ const ev = (gap, e) => { now += gap; events.push({ ts: now, ...e }); return now; };
17
+ const tok = (agent) => { const k = ++n; events.push({ ts: now - 5, agent, kind: 'usage', u: { i: 4 + (k % 7), o: 90 + ((k * 37) % 420), r: 16000 + k * 1100, w: k % 5 === 0 ? 2400 : 0 }, model: 'demo' }); }; // made-up numbers, same shape as real ones
18
+ const user = (text, fr) => ev(0, { agent: 'user', kind: 'user', text, fr });
19
+ const say = (agent, text, fr, gap = 2600) => { const t = ev(gap, { agent, kind: 'say', text, fr }); tok(agent); return t; };
20
+ const order = (from, to, title, text, fr, gap = 2200) => { const t = ev(gap, { agent: from, to, kind: 'order', title, text, fr }); tok(from); return t; };
21
+ const report = (agent, to, text, fr, gap = 2600) => ev(gap, { agent, to, kind: 'report', text, fr });
22
+ // one tool call + its result, `ms` later
23
+ const use = (agent, name, arg, d, out, { err = false, ms = 400, gap = 1500 } = {}) => {
24
+ const id = 'toolu_demo' + ++n, ts = ev(gap, { agent, kind: 'tools', tools: [{ id, name, arg, d }] }); tok(agent);
25
+ if (out != null) events.push({ ts: ts + ms, agent, kind: 'results', items: [{ id, err, out }] });
26
+ events[events.findIndex((e) => e.tools && e.tools[0].id === id)].tools[0].ts = ts;
27
+ };
28
+ const todo = (agent, items, gap = 1200) => use(agent, 'TodoWrite', 'update todos', { kind: 'todo', items: items.map(([c, s]) => ({ c, s })) }, null, { gap });
29
+
30
+ user('The login page is slow and the cart tests keep failing at random. Can you sort it out?',
31
+ 'La page de connexion est lente et les tests du panier échouent au hasard. Tu peux régler ça ?');
32
+ say('main', "On it. I'll split the work: one agent profiles the login, one hunts the flaky cart tests, one reviews the API errors.",
33
+ "Je m'en occupe. Je répartis le travail : un agent profile le login, un autre traque les tests instables du panier, un troisième passe en revue les erreurs de l'API.", 3000);
34
+ todo('main', [['Profile the login flow', 'in_progress'], ['Fix the flaky cart tests', 'pending'], ['Review API error handling', 'pending'], ['Commit the changes', 'pending']]);
35
+ order('main', 'ada', 'Profile the login flow', "Find out why /login takes about 3 seconds. Read-only: don't edit anything. Report what you find, with file and line.",
36
+ "Trouve pourquoi /login prend environ 3 secondes. Lecture seule : ne modifie rien. Rapporte ce que tu trouves, avec fichier et ligne.");
37
+ order('main', 'remy', 'Fix the flaky cart tests', 'The cart tests fail about one run in three. Find the cause, fix it, then run the suite twice to be sure.',
38
+ 'Les tests du panier échouent environ une fois sur trois. Trouve la cause, corrige, puis lance la suite deux fois pour être sûr.', 1200);
39
+ order('main', 'iris', 'Review API error handling', 'Review how src/api handles failures. List the unhandled errors and fix the safe ones. Ask Lena to check the retry logic.',
40
+ "Passe en revue la gestion des erreurs dans src/api. Liste celles qui ne sont pas gérées et corrige les plus sûres. Demande à Lena de vérifier la logique de retry.", 1200);
41
+
42
+ // --- parallel work -------------------------------------------------------------
43
+ use('ada', 'Read', 'src/pages/Login.jsx', { kind: 'read', file: 'src/pages/Login.jsx' }, cat([
44
+ "import { useEffect, useState } from 'react';", "import { useProfile } from '../context/ProfileProvider';", "import { fetchProfile } from '../api/profile';", "import { fetchNetworks } from '../api/networks';", '',
45
+ 'export default function Login() {', ' const profile = useProfile();', ' const [networks, setNetworks] = useState(null);', '', ' useEffect(() => {', ' fetchProfile(); // <-- second call, the provider already fetched it',
46
+ ' fetchNetworks().then(setNetworks);', ' }, []);', '', ' if (!profile || !networks) return <Spinner />;', '', ' return (', ' <main className="login">', ' <LoginForm networks={networks} />', ' </main>', ' );', '}']), { ms: 220, gap: 2400 });
47
+ use('remy', 'Bash', 'npm test -- cart', { kind: 'cmd', text: 'npm test -- cart', note: 'Run the cart tests' },
48
+ ' FAIL src/cart/cart.test.js\n ● cart › updates the total after adding an item\n\n expect(received).toBe(expected)\n\n Expected: 42\n Received: 0\n\n 18 | addItem(item);\n > 19 | expect(total()).toBe(42);\n\nTests: 1 failed, 11 passed, 12 total', { err: true, ms: 2600, gap: 600 });
49
+ use('iris', 'Glob', 'src/api/**/*.js', { kind: 'glob', pattern: 'src/api/**/*.js' }, 'src/api/client.js\nsrc/api/errors.js\nsrc/api/networks.js\nsrc/api/profile.js\nsrc/api/checkout.js', { ms: 90, gap: 700 });
50
+ use('ada', 'Grep', 'fetchProfile', { kind: 'grep', pattern: 'fetchProfile', path: 'src' }, 'src/pages/Login.jsx:10: fetchProfile(); // <-- second call\nsrc/context/ProfileProvider.jsx:18: fetchProfile().then(setProfile);\nsrc/api/profile.js:4:export async function fetchProfile() {', { ms: 130, gap: 1700 });
51
+ use('remy', 'Read', 'src/cart/cart.test.js', { kind: 'read', file: 'src/cart/cart.test.js' }, cat([
52
+ "import { addItem, total, resetCart } from './cart';", "import { waitFor } from '../test-utils';", '', 'beforeEach(() => resetCart());', '', "test('starts empty', () => {", ' expect(total()).toBe(0);', '});', '',
53
+ "test('updates the total after adding an item', async () => {", ' const item = { id: 1, price: 42 };', ' addItem(item);', ' expect(total()).toBe(42);', '});']), { ms: 180, gap: 1800 });
54
+ use('iris', 'Read', 'src/api/errors.js', { kind: 'read', file: 'src/api/errors.js' }, cat([
55
+ "export async function handle(res) {", " if (res.ok) return res.json();", " // TODO: map status codes to friendly messages", " throw new Error('request failed');", '}', '',
56
+ "export async function safe(fn) {", ' try { return await fn(); } catch (e) { /* ignored */ }', '}']), { ms: 160, gap: 1200 });
57
+ use('ada', 'Bash', 'npm run profile -- login', { kind: 'cmd', text: 'npm run profile -- login', note: 'Profile the login page' },
58
+ 'login render: 3120 ms\n ├─ fetchProfile 2410 ms (called 2×)\n ├─ fetchNetworks 380 ms\n └─ paint 330 ms', { ms: 4200, gap: 1400 });
59
+ use('remy', 'Edit', 'src/cart/cart.test.js', { kind: 'edit', file: 'src/cart/cart.test.js', old: ' addItem(item);\n expect(total()).toBe(42);', new: ' addItem(item);\n await waitFor(() => expect(total()).toBe(42));' },
60
+ 'The file src/cart/cart.test.js has been updated.', { ms: 120, gap: 1500 });
61
+ use('iris', 'Grep', 'catch', { kind: 'grep', pattern: 'catch', path: 'src/api' }, 'src/api/errors.js:8: try { return await fn(); } catch (e) { /* ignored */ }\nsrc/api/checkout.js:22: } catch (e) {\nsrc/api/profile.js:11: } catch (e) {}', { ms: 110, gap: 1300 });
62
+ say('ada', "Found it: fetchProfile runs twice and blocks the first render. That's about 2.4 seconds.", "Trouvé : fetchProfile est appelé deux fois et bloque le premier affichage. Ça fait environ 2,4 secondes.", 1700);
63
+ use('remy', 'Bash', 'npm test -- cart', { kind: 'cmd', text: 'npm test -- cart', note: 'Run the cart tests again' },
64
+ ' PASS src/cart/cart.test.js\n ✓ starts empty (3 ms)\n ✓ updates the total after adding an item (12 ms)\n\nTests: 12 passed, 12 total\nTime: 2.1 s', { ms: 2300, gap: 1500 });
65
+ order('iris', 'hugo', 'Retry logic check', 'Check the retry logic in src/api/client.js: any infinite retry, or no backoff?', 'Vérifie la logique de retry dans src/api/client.js : y a-t-il un retry infini, ou pas de backoff ?', 1600);
66
+ report('ada', 'main', 'Login is slow because fetchProfile runs twice (Login.jsx:10 and ProfileProvider.jsx:18) and blocks rendering. Fix: fetch once, in the provider. Expected: 3.1 s down to under 1 s.',
67
+ "Le login est lent car fetchProfile est appelé deux fois (Login.jsx:10 et ProfileProvider.jsx:18) et bloque l'affichage. Correctif : un seul appel, dans le provider. Attendu : de 3,1 s à moins d'une seconde.", 1400);
68
+ use('hugo', 'Read', 'src/api/client.js', { kind: 'read', file: 'src/api/client.js' }, cat([
69
+ "const MAX = 5;", '', 'export async function request(url, opts, attempt = 0) {', ' try {', ' return await fetch(url, opts);', ' } catch (e) {', ' if (attempt >= MAX) throw e;', ' return request(url, opts, attempt + 1); // no delay', ' }', '}']), { ms: 140, gap: 1000 });
70
+ say('remy', "The test didn't wait for the async total. Fixed, and green twice in a row.", "Le test n'attendait pas le total asynchrone. Corrigé, et vert deux fois de suite.", 1600);
71
+ report('remy', 'main', 'Cause: the cart test read the total before the async update finished. Added waitFor. Two consecutive runs pass (12/12).', "Cause : le test lisait le total avant la fin de la mise à jour asynchrone. J'ai ajouté waitFor. Deux exécutions de suite passent (12/12).", 1200);
72
+ use('iris', 'Edit', 'src/api/errors.js', { kind: 'edit', file: 'src/api/errors.js', old: " throw new Error('request failed');", new: " throw new ApiError(res.status, friendly(res.status));" },
73
+ 'The file src/api/errors.js has been updated.', { ms: 120, gap: 1500 });
74
+ say('hugo', "No infinite loop, but there's no backoff: five instant retries hammer the server. And 4xx errors shouldn't retry at all.", "Pas de boucle infinie, mais aucun backoff : cinq retries immédiats martèlent le serveur. Et les erreurs 4xx ne devraient pas être retentées.", 1800);
75
+ report('hugo', 'iris', 'Retries have no backoff and also retry on 4xx. Suggest exponential backoff and stopping on client errors.', 'Les retries n’ont pas de backoff et retentent aussi sur les 4xx. Je suggère un backoff exponentiel et un arrêt sur les erreurs client.', 1200);
76
+ use('iris', 'Bash', 'npm test -- api', { kind: 'cmd', text: 'npm test -- api', note: 'Run the API tests' }, ' PASS src/api/errors.test.js\n PASS src/api/client.test.js\n\nTests: 18 passed, 18 total', { ms: 1100, gap: 1300 });
77
+ report('iris', 'main', 'Two unhandled error paths fixed (profile load, checkout) and retries now back off and stop on 4xx. API tests: 18/18.', 'Deux chemins d’erreur non gérés corrigés (chargement du profil, paiement), et les retries ralentissent et s’arrêtent sur les 4xx. Tests API : 18/18.', 1700);
78
+
79
+ // --- the lead wraps up ----------------------------------------------------------------
80
+ todo('main', [['Profile the login flow', 'completed'], ['Fix the flaky cart tests', 'completed'], ['Review API error handling', 'completed'], ['Commit the changes', 'in_progress']], 2200);
81
+ use('main', 'Edit', 'src/pages/Login.jsx', { kind: 'edit', file: 'src/pages/Login.jsx', old: ' fetchProfile(); // <-- second call, the provider already fetched it\n fetchNetworks().then(setNetworks);', new: ' fetchNetworks().then(setNetworks);' },
82
+ 'The file src/pages/Login.jsx has been updated.', { ms: 120, gap: 1400 });
83
+ use('main', 'Bash', 'npm run profile -- login', { kind: 'cmd', text: 'npm run profile -- login', note: 'Check the login speed after the fix' },
84
+ 'login render: 890 ms\n ├─ fetchProfile 410 ms (called 1×)\n ├─ fetchNetworks 380 ms\n └─ paint 100 ms', { ms: 900, gap: 1200 });
85
+ use('main', 'Bash', 'git commit -am "Fix slow login and flaky cart tests"', { kind: 'cmd', text: 'git commit -am "Fix slow login and flaky cart tests"', note: 'Commit' },
86
+ '[fix/login-and-cart 3f2a9c1] Fix slow login and flaky cart tests\n 5 files changed, 38 insertions(+), 12 deletions(-)', { ms: 300, gap: 1300 });
87
+ say('main', 'Done. Login goes from 3.1 s to 0.9 s, the cart tests pass twice in a row, and two API error paths are handled. Everything is committed on a branch.',
88
+ 'Terminé. Le login passe de 3,1 s à 0,9 s, les tests du panier passent deux fois de suite et deux chemins d’erreur de l’API sont gérés. Tout est commité sur une branche.', 1800);
89
+
90
+ events.sort((a, b) => a.ts - b.ts);
91
+ const merged = []; // same merging rule as the real timeline: nothing to merge here, each call is its own beat
92
+ for (const e of events) merged.push(e);
93
+ const first = merged[0].ts;
94
+ return { id: 'demo', proj: '_demo', agents, events: merged.map((e) => ({ ...e, t: e.ts - first })), durationMs: merged[merged.length - 1].ts - first };
95
+ };
package/media/icon.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "@double_geste/agent-standup",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.22.0",
4
+ "description": "Your Claude Code agents as a live video call, from the terminal: a local page in your browser, plus one-line status for status bars. Free, open source, zero token cost.",
5
+ "bin": {
6
+ "agent-standup": "bin/agent-standup.js"
7
+ },
8
+ "main": "server.js",
9
+ "files": [
10
+ "bin",
11
+ "server.js",
12
+ "demo.js",
13
+ "public",
14
+ "media",
15
+ "LICENSE",
16
+ "README.md"
17
+ ],
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/Atoll92/agent-standup.git"
25
+ },
26
+ "homepage": "https://atoll92.github.io/agent-standup/",
27
+ "bugs": {
28
+ "url": "https://github.com/Atoll92/agent-standup/issues"
29
+ },
30
+ "keywords": [
31
+ "claude-code",
32
+ "claude",
33
+ "agents",
34
+ "sub-agents",
35
+ "monitoring",
36
+ "viewer",
37
+ "statusline",
38
+ "standup"
39
+ ],
40
+ "author": "Double Geste",
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }