Repository navigation
Dev #1010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev #1010
Changes from all commits
f8a9f76
742160b
6628c15
0d43aec
a5580ba
663295f
62d002a
bd91197
2d19ca5
6202097
d180358
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |
| provider: claude | ||
| color: cyan | ||
| promptMode: append | ||
| tools: ["Read", "Glob", "Grep"] | ||
| permissionMode: plan | ||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Permission mode must stay explicit after adding Bash. Line 8 adds As per coding guidelines 🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| @SOUL.md | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |
| provider: claude | ||
| color: red | ||
| promptMode: append | ||
| tools: ["Read", "Glob", "Grep"] | ||
| permissionMode: plan | ||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bash enablement needs explicit permission guard. Line 8 enables As per coding guidelines 🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| @SOUL.md | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |
| provider: claude | ||
| color: magenta | ||
| promptMode: append | ||
| tools: ["Read", "Glob", "Grep"] | ||
| permissionMode: plan | ||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 8 introduces risk without explicit permission pinning.
As per coding guidelines 🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| @SOUL.md | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |||||||
| provider: claude | ||||||||
| color: red | ||||||||
| promptMode: append | ||||||||
| tools: ["Read", "Glob", "Grep"] | ||||||||
| permissionMode: plan | ||||||||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security regression: explicit permission mode is now missing. Line 8 adds Suggested fix tools: ["Read", "Glob", "Grep", "Bash"]
+permissionMode: planAs per coding guidelines 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||
| --- | ||||||||
|
|
||||||||
| @SOUL.md | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |
| provider: claude | ||
| color: cyan | ||
| promptMode: append | ||
| tools: ["Read", "Glob", "Grep"] | ||
| permissionMode: plan | ||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expanded tool capability without explicit permission control. Line 8 adds As per coding guidelines 🤖 Prompt for AI Agents |
||
| --- | ||
|
|
||
| @SOUL.md | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,7 @@ model: haiku | |
| provider: claude | ||
| color: purple | ||
| promptMode: append | ||
| tools: ["Read", "Glob", "Grep"] | ||
| permissionMode: plan | ||
| tools: ["Read", "Glob", "Grep", "Bash"] | ||
| --- | ||
|
|
||
| @SOUL.md | ||
|
|
@@ -17,6 +16,32 @@ Orchestrate real multi-agent deliberation by spawning council members via genie | |
| Architectural decisions are expensive to reverse. Shallow review misses failure modes. Real multi-agent deliberation with distinct reasoning chains catches what single viewpoints miss. | ||
| </mission> | ||
|
|
||
| <spawning> | ||
| Council members MUST be spawned via `genie spawn` — this routes through genie's tmux topology management and places members in the correct session/window. | ||
|
|
||
| **Spawn each selected member:** | ||
| ```bash | ||
| genie spawn council--<member> --team $GENIE_TEAM | ||
| ``` | ||
|
|
||
| **NEVER use the Agent tool to spawn council members.** The Agent tool creates a separate tmux session, breaking the session topology. All spawning goes through `genie spawn`. | ||
|
|
||
| **Post topic to team chat after spawning:** | ||
| ```bash | ||
| genie broadcast "COUNCIL TOPIC: <topic>" --team $GENIE_TEAM | ||
| ``` | ||
|
|
||
| **Send instructions to members:** | ||
| ```bash | ||
| genie send "<instructions>" --to council--<member> --team $GENIE_TEAM | ||
| ``` | ||
|
Comment on lines
+20
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use actual CLI command signatures in orchestration examples. Lines 20-37 document Proposed doc fix-Council members MUST be spawned via `genie spawn` — this routes through genie's tmux topology management and places members in the correct session/window.
+Council members MUST be spawned via `genie agent spawn` — this routes through genie's tmux topology management and places members in the correct session/window.
```bash
-genie spawn council--<member> --team $GENIE_TEAM
+genie agent spawn council--<member> --team $GENIE_TEAM-NEVER use the Agent tool to spawn council members. The Agent tool creates a separate tmux session, breaking the session topology. All spawning goes through -genie broadcast "COUNCIL TOPIC: <topic>" --team $GENIE_TEAM
+genie agent send "COUNCIL TOPIC: <topic>" --broadcast --team $GENIE_TEAM-genie send "<instructions>" --to council--<member> --team $GENIE_TEAM
+genie agent send "<instructions>" --to council--<member> --team $GENIE_TEAMVerify each finding against the current code and only fix it if needed. In |
||
|
|
||
| **Read team chat for responses:** | ||
| ```bash | ||
| genie chat read <convId> | ||
| ``` | ||
| </spawning> | ||
|
|
||
| <routing> | ||
| Not every topic needs all 10 perspectives. Route based on topic: | ||
|
|
||
|
|
@@ -70,4 +95,6 @@ The council produces a structured report with: | |
| - Always synthesize — raw perspectives without interpretation are not useful | ||
| - No voting — no APPROVE/REJECT/MODIFY verdicts. The council thinks; `/review` judges. | ||
| - Dissent is preserved — minority views are captured, never suppressed | ||
| - **NEVER use the Agent tool to spawn members** — always `genie spawn` | ||
| - **NEVER create teams** — use the team you were spawned into (`$GENIE_TEAM`) | ||
| </constraints> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,7 @@ Specialist spawns are ADDITIONS to the default flow (except refactor replacing e | |
| - NEVER write code. `genie work` dispatches engineers who write code. | ||
| - NEVER push to main or master. | ||
| - NEVER use the Agent tool — use `genie work` to dispatch. | ||
| - NEVER pass `--session` to `genie spawn` — the team config resolves the correct tmux session automatically. Passing `--session <team>` creates a separate session, breaking topology. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check spawn command definition and validation logic
echo "=== Spawn command accepts both --team and --session ==="
rg -n -A 2 "\.option.*--session" src/term-commands/agent/spawn.ts
echo ""
echo "=== handleWorkerSpawn sets sessionOverride without validation ==="
rg -n -C 3 "sessionOverride.*options\.session" src/term-commands/agents.ts
echo ""
echo "=== Search for any validation that rejects both flags together ==="
rg -n "session.*team|team.*session" src/term-commands/agents.ts | rg -i "error|throw|reject"Repository: automagik-dev/genie Length of output: 909 🏁 Script executed: cat -n plugins/genie/agents/team-lead/AGENTS.md | sed -n '115,130p'Repository: automagik-dev/genie Length of output: 764 🏁 Script executed: #!/bin/bash
# Get context around handleWorkerSpawn to understand what it does with --team and --session
echo "=== handleWorkerSpawn implementation context ==="
rg -n -B 10 -A 15 "async function handleWorkerSpawn" src/term-commands/agents.ts | head -60Repository: automagik-dev/genie Length of output: 1346 🏁 Script executed: #!/bin/bash
# Check if there's validation that prevents --team and --session together
echo "=== Check for validation before handleWorkerSpawn call ==="
rg -n -B 15 "handleWorkerSpawn" src/term-commands/agents.ts | grep -A 15 "team\|session" | head -40Repository: automagik-dev/genie Length of output: 121 🏁 Script executed: #!/bin/bash
# Find where sessionOverride is used
echo "=== Where is sessionOverride used? ==="
rg -n "sessionOverride" src/term-commands/agents.ts | head -20
echo ""
echo "=== Check the spawn function that uses these params ==="
rg -n "function.*spawn\|export.*spawn" src/genie-commands/ | grep -v "AGENTS.md"Repository: automagik-dev/genie Length of output: 689 🏁 Script executed: #!/bin/bash
# Check what doSpawn or spawn function does with sessionOverride
echo "=== Search for spawn function that accepts sessionOverride param ==="
rg -n "sessionOverride" src/genie-commands/ -A 2 -B 2Repository: automagik-dev/genie Length of output: 131 🏁 Script executed: #!/bin/bash
# Examine the complete flow - find where options are parsed and passed to handleWorkerSpawn
echo "=== Command definition for spawn in agents.ts ==="
rg -n "\.command.*spawn\|\.subcommand.*spawn" src/term-commands/agents.ts -A 30 | head -60
echo ""
echo "=== Look for validation of team vs session flags ==="
rg -n "options\\.team\|options\\.session" src/term-commands/agents.ts | head -20Repository: automagik-dev/genie Length of output: 166 🏁 Script executed: #!/bin/bash
# Check if there's any conditional logic that prevents both flags
echo "=== Check if options.team blocks options.session or vice versa ==="
rg -n "if.*team\|if.*session" src/term-commands/agents.ts -A 3 -B 1 | grep -A 3 -B 1 "team.*session\|session.*team"
echo ""
echo "=== View the actual spawn command definition ==="
rg -n "new Command\|spawn\(\)" src/term-commands/agents.ts | head -10Repository: automagik-dev/genie Length of output: 179 🏁 Script executed: #!/bin/bash
# Find where the spawn command is registered
echo "=== Find spawn command registration ==="
fd -t f "agents.ts" src/term-commands/ -exec grep -l "spawn" {} \;
echo ""
echo "=== Look at the whole agents.ts file structure ==="
wc -l src/term-commands/agents.ts
echo ""
echo "=== Search for Command or command near spawn ==="
rg -n "Command|\.command|\.action" src/term-commands/agents.ts | grep -i spawnRepository: automagik-dev/genie Length of output: 521 🏁 Script executed: #!/bin/bash
# Let's find spawn.ts directly
echo "=== Find spawn.ts file ==="
fd -t f "spawn.ts" src/
echo ""
echo "=== Check spawn.ts for validation ==="
cat -n src/term-commands/agent/spawn.ts | head -80Repository: automagik-dev/genie Length of output: 2345 🏁 Script executed: #!/bin/bash
# Final verification: confirm that sessionOverride completely bypasses team resolution
echo "=== Confirm sessionOverride priority in spawn execution ==="
rg -n -B 5 -A 5 "let sessionName = sessionOverride" src/term-commands/agents.tsRepository: automagik-dev/genie Length of output: 479 Add validation to reject The constraint documented in AGENTS.md (line 121) prohibits passing Add validation in 🤖 Prompt for AI Agents |
||
| - NEVER poll faster than every 60 seconds. Always `sleep 60` before `genie status`. | ||
| - NEVER run more than 10 status checks per wave without progress. | ||
| - If `genie status` returns "No state found" → run `genie work <slug>` immediately. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,20 @@ genie ls --json # Agent state from PG | |
| NEVER use `Agent` to spawn agents — use `genie spawn` instead. | ||
| NEVER use `TeamCreate` or `TeamDelete` — use `genie team create` / `genie team disband` instead. | ||
|
|
||
| ## Spawn Session Rule | ||
|
|
||
| NEVER pass `--session <team-name>` to `genie spawn`. The team config already stores the correct `tmuxSessionName` (resolved at team creation from the parent session). Passing `--session` overrides this and creates a separate tmux session, breaking the topology. | ||
|
|
||
| ```bash | ||
| # WRONG — creates separate session | ||
| genie spawn reviewer --team my-team --session my-team | ||
|
|
||
| # CORRECT — uses team's configured session | ||
| genie spawn reviewer --team my-team | ||
| ``` | ||
|
|
||
| The `--session` flag is for rare manual overrides only. When `--team` is set, let genie resolve the session from the team config. | ||
|
Comment on lines
+22
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rule clearly documented, but enforcement missing. The "Spawn Session Rule" provides clear rationale and examples. However, as noted in the review of Consider adding validation in 🤖 Prompt for AI Agents |
||
|
|
||
| ## Post-Dispatch Monitoring | ||
|
|
||
| After `genie team create` or `genie spawn`, use ONLY structured primitives. A hook enforces this automatically. | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,120 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'bun:test'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { tmpdir } from 'node:os'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { join } from 'node:path'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { checkForUpdates, compareVersions } from './brain.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Tests for the cache-only checkForUpdates function. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * checkForUpdates reads a JSON cache file synchronously. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * It never makes network calls and never throws. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * We use a temp directory so tests never touch the real ~/.genie/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * and work in CI without permission issues. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('checkForUpdates', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let tempDir: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cachePath: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeAll(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tempDir = join(tmpdir(), `brain-test-${Date.now()}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdirSync(tempDir, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachePath = join(tempDir, 'brain-version-check.json'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterAll(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rmSync(tempDir, { recursive: true, force: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* best effort */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rmSync(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* ok */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+17
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider using Per coding guidelines, test fixtures should use tmpdir with cleanup in This is minor since the current approach works, but ♻️ Suggested adjustment describe('checkForUpdates', () => {
let tempDir: string;
let cachePath: string;
- beforeAll(() => {
+ beforeEach(() => {
tempDir = join(tmpdir(), `brain-test-${Date.now()}`);
mkdirSync(tempDir, { recursive: true });
cachePath = join(tempDir, 'brain-version-check.json');
});
- afterAll(() => {
+ afterEach(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
/* best effort */
}
});
-
- beforeEach(() => {
- try {
- rmSync(cachePath);
- } catch {
- /* ok */
- }
- });As per coding guidelines: "Use tmpdir with cleanup in afterEach for test fixtures" 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('returns updateAvailable false when cache does not exist', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkForUpdates(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(result).toEqual({ updateAvailable: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('returns updateAvailable true when cache says so', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachePath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkedAt: new Date().toISOString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| localVersion: '1.260401.1', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latestTag: 'v0.260403.2', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latestVersion: '0.260403.2', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateAvailable: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkForUpdates(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(result).toEqual({ updateAvailable: true, latestVersion: '0.260403.2' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('returns updateAvailable false when cache says no update', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachePath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkedAt: new Date().toISOString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| localVersion: '1.260403.2', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latestTag: 'v0.260403.2', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latestVersion: '0.260403.2', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateAvailable: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkForUpdates(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(result).toEqual({ updateAvailable: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('returns updateAvailable false when cache is invalid JSON', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync(cachePath, 'not valid json{{{'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkForUpdates(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(result).toEqual({ updateAvailable: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('never throws regardless of cache content', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync(cachePath, JSON.stringify({ random: 'garbage', updateAvailable: 'yes' })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // updateAvailable is string 'yes' which is truthy but latestVersion is undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = checkForUpdates(cachePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(result.updateAvailable).toBe(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('compareVersions', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('equal versions return 0', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.1', '260403.1')).toBe(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('a > b returns positive', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.2', '260403.1')).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('a < b returns negative', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.1', '260403.2')).toBeLessThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('handles multi-digit segments correctly (the bug)', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // String comparison: "260403.9" > "260403.10" → true (wrong) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Numeric comparison: 9 < 10 → correct | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.10', '260403.9')).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.9', '260403.10')).toBeLessThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('handles different segment counts', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403.1.1', '260403.1')).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260403', '260403.0')).toBe(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('handles major version differences', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(compareVersions('260404.1', '260403.99')).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding Bash requires explicit non-permissive permission mode.
Line 8 is fine only if permission behavior is pinned. Right now
permissionModeis absent, which can make this agent run too permissively.As per coding guidelines
plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.🤖 Prompt for AI Agents