bots-garden/sidekickspublic Fork
⑂ 8ef4097
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/sidekicks.git
git clone ssh://git@rickub.com/bots-garden/sidekicks.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

🛟 Updated.

k33g committed 2026-10-30T13:37:45+01:00 Browse files
8ef4097 parent: f443a08
modified .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py +7 -5
@@ -1,7 +1,7 @@
11 """The description of docs/diagrams/packages.{drawio,svg} (Sidekicks, ADR 0003, 0004, 0006 and 0007).
22
33 python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams
4- rsvg-convert -w 1440 -h 800 -o docs/diagrams/packages.png docs/diagrams/packages.svg
4+ rsvg-convert -w 1690 -h 800 -o docs/diagrams/packages.png docs/diagrams/packages.svg
55
66 Blue boxes exist; grey boxes are planned (their milestone is written in them).
77 """
@@ -12,11 +12,11 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."
1212 from flowgen import Diagram
1313
1414 OUT = sys.argv[1] if len(sys.argv) > 1 else "docs/diagrams"
15-W, COLUMNS = 230, (30, 310, 590, 870, 1150)
15+W, COLUMNS = 230, (30, 310, 590, 870, 1150, 1430)
1616 CENTERS = [x + W // 2 for x in COLUMNS]
1717
1818 d = Diagram("packages", "Sidekicks — packages and dependencies",
19- "arrows are imports · all packages exist (M4) · only main.go imports Wails", width=1410, height=800)
19+ "arrows are imports · all packages exist (v0.1) · only main.go imports Wails", width=1690, height=800)
2020
2121 # Row 1: the entry points.
2222 d.box(COLUMNS[0], 100, W, 115, "user", "💻", "frontend/", ["Svelte 5 · panels · chat · @ /", "files · xterm.js · bindings"])
@@ -24,6 +24,8 @@ d.box(COLUMNS[2], 100, W, 115, "decider", "🔀", "internal/app", ["Agent · Cha
2424 d.box(COLUMNS[4], 100, W, 115, "hook", "⚙️", "main.go (root)", ["flags · Wails app · window", "events · file dialog"])
2525 d.edge([(COLUMNS[0] + W, 157), (COLUMNS[2], 157)], "bindings · events", at=(450, 157), lw=130, both=True)
2626 d.edge([(COLUMNS[4], 157), (COLUMNS[2] + W, 157)], "creates", at=(1010, 157), lw=70)
27+d.box(COLUMNS[5], 100, W, 115, "agent", "🧰", "internal/shellpath", ["PATH of the user's shell", "for apps opened from the Finder"])
28+d.edge([(COLUMNS[4] + W, 157), (COLUMNS[5], 157)])
2729
2830 # Row 2: what internal/app uses.
2931 d.box(COLUMNS[0], 300, W, 110, "agent", "💬", "internal/channels", ["channels · direct · routing @", "unseen context · queue · perms"])
@@ -31,7 +33,7 @@ d.box(COLUMNS[1], 300, W, 110, "agent", "🤖", "internal/agents", ["lazy start
3133 d.box(COLUMNS[2], 300, W, 110, "agent", "📋", "internal/config", ["load · validate · locate", "default · save · watch"])
3234 d.box(COLUMNS[3], 300, W, 110, "agent", "🧰", "internal/prefs", ["last opened file", "recent files (JSON)"])
3335 d.box(COLUMNS[4], 300, W, 110, "agent", "🧰", "internal/workspace", ["files · watch · terminals", "local (pty) · docker exec"])
34-for cx in CENTERS:
36+for cx in CENTERS[:5]:
3537 d.edge([(705, 215), (705, 255), (cx, 255), (cx, 300)])
3638 d.edge([(COLUMNS[0] + W, 355), (COLUMNS[1], 355)])
3739 d.edge([(COLUMNS[1] + W, 355), (COLUMNS[2], 355)])
@@ -56,5 +58,5 @@ d.edge([(CENTERS[4], 410), (CENTERS[4], 490)])
5658 d.edge([(CENTERS[0], 595), (CENTERS[0], 670)])
5759 d.edge([(CENTERS[1], 595), (CENTERS[1], 670)], "stdio", at=(CENTERS[1] + 45, 632), lw=50)
5860 d.edge([(CENTERS[2], 595), (CENTERS[2], 670)])
59-d.text(705, 765, "channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.", 12.5, italic=True)
61+d.text(845, 765, "channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.", 12.5, italic=True)
6062 d.write(OUT)
@@ -1,7 +1,7 @@
1 """The description of docs/diagrams/packages.{drawio,svg} (Sidekicks, ADR 0003, 0004, 0006 and 0007).1 """The description of docs/diagrams/packages.{drawio,svg} (Sidekicks, ADR 0003, 0004, 0006 and 0007).
2 2
3 python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams3 python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams
4- rsvg-convert -w 1440 -h 800 -o docs/diagrams/packages.png docs/diagrams/packages.svg4+ rsvg-convert -w 1690 -h 800 -o docs/diagrams/packages.png docs/diagrams/packages.svg
5 5
6 Blue boxes exist; grey boxes are planned (their milestone is written in them).6 Blue boxes exist; grey boxes are planned (their milestone is written in them).
7 """7 """
@@ -12,11 +12,11 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."
12 from flowgen import Diagram12 from flowgen import Diagram
13 13
14 OUT = sys.argv[1] if len(sys.argv) > 1 else "docs/diagrams"14 OUT = sys.argv[1] if len(sys.argv) > 1 else "docs/diagrams"
15-W, COLUMNS = 230, (30, 310, 590, 870, 1150)15+W, COLUMNS = 230, (30, 310, 590, 870, 1150, 1430)
16 CENTERS = [x + W // 2 for x in COLUMNS]16 CENTERS = [x + W // 2 for x in COLUMNS]
17 17
18 d = Diagram("packages", "Sidekicks — packages and dependencies",18 d = Diagram("packages", "Sidekicks — packages and dependencies",
19- "arrows are imports · all packages exist (M4) · only main.go imports Wails", width=1410, height=800)19+ "arrows are imports · all packages exist (v0.1) · only main.go imports Wails", width=1690, height=800)
20 20
21 # Row 1: the entry points.21 # Row 1: the entry points.
22 d.box(COLUMNS[0], 100, W, 115, "user", "💻", "frontend/", ["Svelte 5 · panels · chat · @ /", "files · xterm.js · bindings"])22 d.box(COLUMNS[0], 100, W, 115, "user", "💻", "frontend/", ["Svelte 5 · panels · chat · @ /", "files · xterm.js · bindings"])
@@ -24,6 +24,8 @@ d.box(COLUMNS[2], 100, W, 115, "decider", "🔀", "internal/app", ["Agent · Cha
24 d.box(COLUMNS[4], 100, W, 115, "hook", "⚙️", "main.go (root)", ["flags · Wails app · window", "events · file dialog"])24 d.box(COLUMNS[4], 100, W, 115, "hook", "⚙️", "main.go (root)", ["flags · Wails app · window", "events · file dialog"])
25 d.edge([(COLUMNS[0] + W, 157), (COLUMNS[2], 157)], "bindings · events", at=(450, 157), lw=130, both=True)25 d.edge([(COLUMNS[0] + W, 157), (COLUMNS[2], 157)], "bindings · events", at=(450, 157), lw=130, both=True)
26 d.edge([(COLUMNS[4], 157), (COLUMNS[2] + W, 157)], "creates", at=(1010, 157), lw=70)26 d.edge([(COLUMNS[4], 157), (COLUMNS[2] + W, 157)], "creates", at=(1010, 157), lw=70)
27+d.box(COLUMNS[5], 100, W, 115, "agent", "🧰", "internal/shellpath", ["PATH of the user's shell", "for apps opened from the Finder"])
28+d.edge([(COLUMNS[4] + W, 157), (COLUMNS[5], 157)])
27 29
28 # Row 2: what internal/app uses.30 # Row 2: what internal/app uses.
29 d.box(COLUMNS[0], 300, W, 110, "agent", "💬", "internal/channels", ["channels · direct · routing @", "unseen context · queue · perms"])31 d.box(COLUMNS[0], 300, W, 110, "agent", "💬", "internal/channels", ["channels · direct · routing @", "unseen context · queue · perms"])
@@ -31,7 +33,7 @@ d.box(COLUMNS[1], 300, W, 110, "agent", "🤖", "internal/agents", ["lazy start
31 d.box(COLUMNS[2], 300, W, 110, "agent", "📋", "internal/config", ["load · validate · locate", "default · save · watch"])33 d.box(COLUMNS[2], 300, W, 110, "agent", "📋", "internal/config", ["load · validate · locate", "default · save · watch"])
32 d.box(COLUMNS[3], 300, W, 110, "agent", "🧰", "internal/prefs", ["last opened file", "recent files (JSON)"])34 d.box(COLUMNS[3], 300, W, 110, "agent", "🧰", "internal/prefs", ["last opened file", "recent files (JSON)"])
33 d.box(COLUMNS[4], 300, W, 110, "agent", "🧰", "internal/workspace", ["files · watch · terminals", "local (pty) · docker exec"])35 d.box(COLUMNS[4], 300, W, 110, "agent", "🧰", "internal/workspace", ["files · watch · terminals", "local (pty) · docker exec"])
34-for cx in CENTERS:36+for cx in CENTERS[:5]:
35 d.edge([(705, 215), (705, 255), (cx, 255), (cx, 300)])37 d.edge([(705, 215), (705, 255), (cx, 255), (cx, 300)])
36 d.edge([(COLUMNS[0] + W, 355), (COLUMNS[1], 355)])38 d.edge([(COLUMNS[0] + W, 355), (COLUMNS[1], 355)])
37 d.edge([(COLUMNS[1] + W, 355), (COLUMNS[2], 355)])39 d.edge([(COLUMNS[1] + W, 355), (COLUMNS[2], 355)])
@@ -56,5 +58,5 @@ d.edge([(CENTERS[4], 410), (CENTERS[4], 490)])
56 d.edge([(CENTERS[0], 595), (CENTERS[0], 670)])58 d.edge([(CENTERS[0], 595), (CENTERS[0], 670)])
57 d.edge([(CENTERS[1], 595), (CENTERS[1], 670)], "stdio", at=(CENTERS[1] + 45, 632), lw=50)59 d.edge([(CENTERS[1], 595), (CENTERS[1], 670)], "stdio", at=(CENTERS[1] + 45, 632), lw=50)
58 d.edge([(CENTERS[2], 595), (CENTERS[2], 670)])60 d.edge([(CENTERS[2], 595), (CENTERS[2], 670)])
59-d.text(705, 765, "channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.", 12.5, italic=True)61+d.text(845, 765, "channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.", 12.5, italic=True)
60 d.write(OUT)62 d.write(OUT)
modified .github/workflows/ci.yml +1 -1
@@ -8,7 +8,7 @@
88 # needs GTK >= 4.10 (GtkFileDialog), which older Ubuntu releases lack.
99 # Rickub runs it as an ordinary GitHub Actions workflow, on every push and
1010 # every merge request. No workflow_dispatch: Rickub's dispatch API fires every
11-# dispatchable workflow of a ref (see mini-me's release.yml).
11+# dispatchable workflow of a ref, and release.yml must stay the only one.
1212 name: CI
1313
1414 on:
@@ -8,7 +8,7 @@
8 # needs GTK >= 4.10 (GtkFileDialog), which older Ubuntu releases lack.8 # needs GTK >= 4.10 (GtkFileDialog), which older Ubuntu releases lack.
9 # Rickub runs it as an ordinary GitHub Actions workflow, on every push and9 # Rickub runs it as an ordinary GitHub Actions workflow, on every push and
10 # every merge request. No workflow_dispatch: Rickub's dispatch API fires every10 # every merge request. No workflow_dispatch: Rickub's dispatch API fires every
11-# dispatchable workflow of a ref (see mini-me's release.yml).11+# dispatchable workflow of a ref, and release.yml must stay the only one.
12 name: CI12 name: CI
13 13
14 on:14 on:
added .github/workflows/release.yml +70 -0
new file mode 100644
@@ -0,0 +1,70 @@
1+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+#
3+# Release: when a tag v<version> is pushed (scripts/tag-release.sh), creates
4+# the release on Rickub with its notes, and nothing else. The packages are
5+# built on the maintainer's Mac (scripts/build-release.sh) and attached to the
6+# release by hand: Rickub has Linux x86-64 runners only. See RELEASING.md.
7+#
8+# The job token needs `contents: write` to create a release;
9+# softprops/action-gh-release talks to Rickub's GitHub-compatible API behind
10+# $GITHUB_API_URL (as in rickub/cli's release.yml).
11+name: Release
12+
13+on:
14+ push:
15+ tags:
16+ - "v*"
17+
18+permissions:
19+ contents: write
20+
21+jobs:
22+ release:
23+ runs-on: ubuntu-latest
24+ steps:
25+ - name: Checkout
26+ uses: actions/checkout@v4
27+
28+ - name: The tag is the version of build/config.yml
29+ id: version
30+ run: |
31+ set -eu
32+ version="$(scripts/package.sh version)"
33+ if [ "v$version" != "$GITHUB_REF_NAME" ]; then
34+ echo "::error::The tag is $GITHUB_REF_NAME but build/config.yml says $version."
35+ exit 1
36+ fi
37+ echo "version=$version" >> "$GITHUB_OUTPUT"
38+
39+ - name: Release notes
40+ id: notes
41+ env:
42+ VERSION: ${{ steps.version.outputs.version }}
43+ run: |
44+ set -eu
45+ base="${RICKUB_GITHUB_SERVER_URL:-https://rickub.com}/${GITHUB_REPOSITORY}"
46+ {
47+ echo "## Sidekicks $VERSION"
48+ echo
49+ echo "Commit \`$GITHUB_SHA\`."
50+ echo
51+ echo "Install: [English]($base/blob/$GITHUB_REF_NAME/docs/en/how-to/install-sidekicks.md) · [Français]($base/blob/$GITHUB_REF_NAME/docs/fr/how-to/installer-sidekicks.md)"
52+ echo
53+ echo "| File | System |"
54+ echo "| --- | --- |"
55+ for name in $(scripts/package.sh names macos universal); do echo "| \`$name\` | macOS 13 or later (Apple Silicon and Intel) |"; done
56+ for name in $(scripts/package.sh names windows amd64); do echo "| \`$name\` | Windows x86-64 |"; done
57+ for name in $(scripts/package.sh names linux amd64); do echo "| \`$name\` | Linux x86-64 |"; done
58+ echo "| \`SHA256SUMS\` | checksums: \`sha256sum -c SHA256SUMS --ignore-missing\` (\`shasum -a 256 -c\` on macOS) |"
59+ } > "$RUNNER_TEMP/release-notes.md"
60+ cat "$RUNNER_TEMP/release-notes.md"
61+ echo "path=$RUNNER_TEMP/release-notes.md" >> "$GITHUB_OUTPUT"
62+
63+ - name: Create the release
64+ uses: softprops/action-gh-release@v2
65+ with:
66+ tag_name: ${{ github.ref_name }}
67+ name: Sidekicks ${{ github.ref_name }}
68+ body_path: ${{ steps.notes.outputs.path }}
69+ draft: false
70+ prerelease: false
new file mode 100644
@@ -0,0 +1,70 @@
1+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+#
3+# Release: when a tag v<version> is pushed (scripts/tag-release.sh), creates
4+# the release on Rickub with its notes, and nothing else. The packages are
5+# built on the maintainer's Mac (scripts/build-release.sh) and attached to the
6+# release by hand: Rickub has Linux x86-64 runners only. See RELEASING.md.
7+#
8+# The job token needs `contents: write` to create a release;
9+# softprops/action-gh-release talks to Rickub's GitHub-compatible API behind
10+# $GITHUB_API_URL (as in rickub/cli's release.yml).
11+name: Release
12+
13+on:
14+ push:
15+ tags:
16+ - "v*"
17+
18+permissions:
19+ contents: write
20+
21+jobs:
22+ release:
23+ runs-on: ubuntu-latest
24+ steps:
25+ - name: Checkout
26+ uses: actions/checkout@v4
27+
28+ - name: The tag is the version of build/config.yml
29+ id: version
30+ run: |
31+ set -eu
32+ version="$(scripts/package.sh version)"
33+ if [ "v$version" != "$GITHUB_REF_NAME" ]; then
34+ echo "::error::The tag is $GITHUB_REF_NAME but build/config.yml says $version."
35+ exit 1
36+ fi
37+ echo "version=$version" >> "$GITHUB_OUTPUT"
38+
39+ - name: Release notes
40+ id: notes
41+ env:
42+ VERSION: ${{ steps.version.outputs.version }}
43+ run: |
44+ set -eu
45+ base="${RICKUB_GITHUB_SERVER_URL:-https://rickub.com}/${GITHUB_REPOSITORY}"
46+ {
47+ echo "## Sidekicks $VERSION"
48+ echo
49+ echo "Commit \`$GITHUB_SHA\`."
50+ echo
51+ echo "Install: [English]($base/blob/$GITHUB_REF_NAME/docs/en/how-to/install-sidekicks.md) · [Français]($base/blob/$GITHUB_REF_NAME/docs/fr/how-to/installer-sidekicks.md)"
52+ echo
53+ echo "| File | System |"
54+ echo "| --- | --- |"
55+ for name in $(scripts/package.sh names macos universal); do echo "| \`$name\` | macOS 13 or later (Apple Silicon and Intel) |"; done
56+ for name in $(scripts/package.sh names windows amd64); do echo "| \`$name\` | Windows x86-64 |"; done
57+ for name in $(scripts/package.sh names linux amd64); do echo "| \`$name\` | Linux x86-64 |"; done
58+ echo "| \`SHA256SUMS\` | checksums: \`sha256sum -c SHA256SUMS --ignore-missing\` (\`shasum -a 256 -c\` on macOS) |"
59+ } > "$RUNNER_TEMP/release-notes.md"
60+ cat "$RUNNER_TEMP/release-notes.md"
61+ echo "path=$RUNNER_TEMP/release-notes.md" >> "$GITHUB_OUTPUT"
62+
63+ - name: Create the release
64+ uses: softprops/action-gh-release@v2
65+ with:
66+ tag_name: ${{ github.ref_name }}
67+ name: Sidekicks ${{ github.ref_name }}
68+ body_path: ${{ steps.notes.outputs.path }}
69+ draft: false
70+ prerelease: false
modified .gitignore +7 -0
@@ -8,6 +8,13 @@ bin/
88 frontend/dist
99 frontend/node_modules
1010 build/linux/appimage/build
11+build/linux/sidekicks.desktop
12+# Created by `wails3 task common:update:build-assets`; Sidekicks has no iOS build
13+build/ios/
14+build/linux/appimage/sidekicks
15+build/linux/appimage/sidekicks.png
16+# Release packages (make package-*)
17+dist/
1118 build/windows/nsis/MicrosoftEdgeWebview2Setup.exe
1219 # pnpm content-addressable store (created next to the repository when it sits on another file system)
1320 .pnpm-store/
@@ -8,6 +8,13 @@ bin/
8 frontend/dist8 frontend/dist
9 frontend/node_modules9 frontend/node_modules
10 build/linux/appimage/build10 build/linux/appimage/build
11+build/linux/sidekicks.desktop
12+# Created by `wails3 task common:update:build-assets`; Sidekicks has no iOS build
13+build/ios/
14+build/linux/appimage/sidekicks
15+build/linux/appimage/sidekicks.png
16+# Release packages (make package-*)
17+dist/
11 build/windows/nsis/MicrosoftEdgeWebview2Setup.exe18 build/windows/nsis/MicrosoftEdgeWebview2Setup.exe
12 # pnpm content-addressable store (created next to the repository when it sits on another file system)19 # pnpm content-addressable store (created next to the repository when it sits on another file system)
13 .pnpm-store/20 .pnpm-store/
added .memory/handoffs/2026-10-30-logo-and-ci.md +26 -0
new file mode 100644
@@ -0,0 +1,26 @@
1+# Handoff — 2026-10-30 — Logo, English wireframes, CI green
2+
3+## State
4+
5+- CI (`.github/workflows/ci.yml`) is green on Rickub.
6+- Logo "Orbite" chosen by the owner: sources in `docs/design/logo/` and `build/appicon.svg`; app icons regenerated (PNG, icns, ico, and `Assets.car` regenerated by the owner on the Mac); the welcome screen shows it (`frontend/src/lib/layout/Logo.svelte`, `--logo-*` tokens in `style.css`); README shows it (light/dark `<picture>`).
7+- `docs/design/` wireframes are in English (`wireframes.py` → SVG/PNG/drawio).
8+- `make check` passes. Nothing committed: the owner commits.
9+
10+## In flight
11+
12+Nothing.
13+
14+## Next steps
15+
16+1. Milestone "v0.1 · Première release" (#33–#37) and #38 (qlty kit), when the owner says so.
17+2. Optional: `build/darwin/dmg-file-icon.*` still carry the Wails icon.
18+
19+## Open questions / blockers
20+
21+None.
22+
23+## Watch out for
24+
25+- Regenerating icons: `rsvg-convert` then `wails3 generate icons …` (docs/design/README.md, Logo); `Assets.car` only on macOS.
26+- To change a wireframe, edit `docs/design/wireframes.py`, never the generated SVG/drawio.
new file mode 100644
@@ -0,0 +1,26 @@
1+# Handoff — 2026-10-30 — Logo, English wireframes, CI green
2+
3+## State
4+
5+- CI (`.github/workflows/ci.yml`) is green on Rickub.
6+- Logo "Orbite" chosen by the owner: sources in `docs/design/logo/` and `build/appicon.svg`; app icons regenerated (PNG, icns, ico, and `Assets.car` regenerated by the owner on the Mac); the welcome screen shows it (`frontend/src/lib/layout/Logo.svelte`, `--logo-*` tokens in `style.css`); README shows it (light/dark `<picture>`).
7+- `docs/design/` wireframes are in English (`wireframes.py` → SVG/PNG/drawio).
8+- `make check` passes. Nothing committed: the owner commits.
9+
10+## In flight
11+
12+Nothing.
13+
14+## Next steps
15+
16+1. Milestone "v0.1 · Première release" (#33–#37) and #38 (qlty kit), when the owner says so.
17+2. Optional: `build/darwin/dmg-file-icon.*` still carry the Wails icon.
18+
19+## Open questions / blockers
20+
21+None.
22+
23+## Watch out for
24+
25+- Regenerating icons: `rsvg-convert` then `wails3 generate icons …` (docs/design/README.md, Logo); `Assets.car` only on macOS.
26+- To change a wireframe, edit `docs/design/wireframes.py`, never the generated SVG/drawio.
added .memory/handoffs/2026-10-30-v0.1-release.md +28 -0
new file mode 100644
@@ -0,0 +1,28 @@
1+# Handoff — 2026-10-30 — v0.1 · Première release
2+
3+## State
4+
5+Everything of milestone v0.1 is implemented on `feature/1st-release` (not committed: the owner commits). `make check` is green. Tickets #33, #35, #36, #37 have their tasks closed and a comment, but stay open until the owner validates on the Mac; #34 has the guide and waits for the owner's run and report.
6+
7+## In flight
8+
9+Nothing half-done in the code.
10+
11+## Next steps
12+
13+1. Owner, on the Mac: `make check`, `make dev`, then the v0.1 checks of `docs/en/how-to/run-the-end-to-end-scenarios.md` (section 7) and the six scenarios; post the report on #34.
14+2. Owner, on the Mac: `make package-macos`, install the .dmg, check the Gatekeeper procedure and that an app opened from the Finder finds `mm` (shell PATH).
15+3. Release v0.1.0 as in RELEASING.md: `make release-build` on the Mac (needs Docker running, `brew install makensis zip`), `make release-tag` (from an up-to-date `main`), check that the tag push created the release on Rickub, attach the files of dist/ by hand.
16+4. Close #33–#37 and the milestone once validated; then #38 / #40.
17+
18+## Open questions / blockers
19+
20+- The release workflow (tag push → softprops creates the release) has not run on Rickub yet.
21+- Linux amd64 packages are built in Docker with emulation on Apple Silicon: slow, not tried on the Mac yet.
22+
23+## Watch out for
24+
25+- `wails3 task common:update:build-assets` recreates `build/ios/` (ignored) and rewrites Info.plist etc.; `wails3 task linux:*` regenerates `build/darwin/icons.icns` on Linux (same source PNG) — `Assets.car` is only rebuilt on macOS.
26+- Packaging Linux needs `GOARCH` set (nfpm `${GOARCH}`), done by `scripts/package.sh`; AppImage needs `file`; the desktop entry must not contain whitespace-only lines (appimagetool rejects it).
27+- Vitest leaves CSS empty: `?raw` of style.css works only because `test.css.include` lists it.
28+- `/etc/sandbox-persistent.sh` adds `~/go/bin` to PATH idempotently (a non-idempotent line broke `TestInstallTestConfigWritesAValidConfiguration`, whose script re-sources it).
new file mode 100644
@@ -0,0 +1,28 @@
1+# Handoff — 2026-10-30 — v0.1 · Première release
2+
3+## State
4+
5+Everything of milestone v0.1 is implemented on `feature/1st-release` (not committed: the owner commits). `make check` is green. Tickets #33, #35, #36, #37 have their tasks closed and a comment, but stay open until the owner validates on the Mac; #34 has the guide and waits for the owner's run and report.
6+
7+## In flight
8+
9+Nothing half-done in the code.
10+
11+## Next steps
12+
13+1. Owner, on the Mac: `make check`, `make dev`, then the v0.1 checks of `docs/en/how-to/run-the-end-to-end-scenarios.md` (section 7) and the six scenarios; post the report on #34.
14+2. Owner, on the Mac: `make package-macos`, install the .dmg, check the Gatekeeper procedure and that an app opened from the Finder finds `mm` (shell PATH).
15+3. Release v0.1.0 as in RELEASING.md: `make release-build` on the Mac (needs Docker running, `brew install makensis zip`), `make release-tag` (from an up-to-date `main`), check that the tag push created the release on Rickub, attach the files of dist/ by hand.
16+4. Close #33–#37 and the milestone once validated; then #38 / #40.
17+
18+## Open questions / blockers
19+
20+- The release workflow (tag push → softprops creates the release) has not run on Rickub yet.
21+- Linux amd64 packages are built in Docker with emulation on Apple Silicon: slow, not tried on the Mac yet.
22+
23+## Watch out for
24+
25+- `wails3 task common:update:build-assets` recreates `build/ios/` (ignored) and rewrites Info.plist etc.; `wails3 task linux:*` regenerates `build/darwin/icons.icns` on Linux (same source PNG) — `Assets.car` is only rebuilt on macOS.
26+- Packaging Linux needs `GOARCH` set (nfpm `${GOARCH}`), done by `scripts/package.sh`; AppImage needs `file`; the desktop entry must not contain whitespace-only lines (appimagetool rejects it).
27+- Vitest leaves CSS empty: `?raw` of style.css works only because `test.css.include` lists it.
28+- `/etc/sandbox-persistent.sh` adds `~/go/bin` to PATH idempotently (a non-idempotent line broke `TestInstallTestConfigWritesAValidConfiguration`, whose script re-sources it).
modified .memory/history.md +28 -0
@@ -160,3 +160,31 @@
160160 - **CI follow-up**: `node: command not found` — Rickub's runner runs JavaScript actions inside the job container; `nodejs` added to the first (bash-only) step.
161161 - **CI follow-up 2**: `actions/setup-go` on Rickub ignored `go-version-file` and found no Go. setup-go and setup-node removed: Go installed from go.dev (version from go.mod, dpkg arch), node and npm from Ubuntu 26.04 (node 22.22); only actions/checkout kept.
162162 - **CI follow-up 3**: lint, Go tests, svelte-check and vitest pass on Rickub; the shell tests failed for want of `jq` (scripts/agents.sh) — jq added to the apt packages.
163+
164+## 2026-10-30 — Logo
165+
166+- **Goal**: owner asked for logo proposals while the CI ran; five concepts drawn on a private design canvas (Duo, Orbite, La bande, Le compagnon, S en deux voix); owner chose **02 · Orbite** (centre dot = the user, teal and coral dots in orbit = the agents).
167+- **Changes**: `docs/design/logo/` (mark light/dark, favicon without the ring, app icon copy); `build/appicon.svg` (new source: 1024 canvas, 824 ink tile, macOS grid) → `build/appicon.png`, `build/darwin/icons.icns`, `build/windows/icon.ico` regenerated with `wails3 generate icons`; `build/appicon.icon` glyph replaced (`Assets/sidekicks-mark.svg`, ink automatic-gradient fill, Wails default glyph removed); README shows the mark (light/dark `<picture>`); `docs/design/README.md` Logo section with the regeneration commands.
168+- **Not done here**: `build/darwin/Assets.car` is still the Wails one — it can only be rebuilt on macOS (`-iconcomposerinput appicon.icon -macassetdir darwin`); `dmg-file-icon.*` untouched.
169+- **Follow-up**: owner regenerated `build/darwin/Assets.car` on the Mac. The welcome screen (no channel open, i.e. at launch) now shows the mark above "Sidekicks": `frontend/src/lib/layout/Logo.svelte` (inline SVG, `$props.id()` for the mask id), colours from new `--logo-*` tokens in `style.css` (light and dark). svelte-check, vitest and licence headers pass.
170+- **Follow-up 2**: owner wants the graphics in `docs/design/` in English. `wireframes.py` texts translated (window, messages, notes, titles; the sample `AGENTS.md` stays a French-typography style guide), the six SVG/PNG and `wireframes.drawio` regenerated, `docs/design/README.md` screen names and language sentence updated.
171+
172+## 2026-10-30 — Session closed
173+
174+- **CI**: owner confirms the run is green on Rickub after the `jq` fix (container ubuntu:26.04, Go from go.dev, node/npm/jq from apt).
175+- **Logo work** (above) checked locally: `make check` passes (golangci-lint, Go tests, svelte-check, vitest 63, agents.sh 15, licence headers).
176+
177+## 2026-10-30 — v0.1 · Première release (implementation)
178+
179+- **Goal**: milestone v0.1 (#33 onboarding, #34 end-to-end scenarios, #35 keyboard and accessibility, #36 macOS packaging) plus, asked mid-way, Linux and Windows packages and a workflow that builds them and attaches them to a release. Owner's choices: assistant for local + Docker + sbx; scenarios as a step-by-step guide + report; no Apple Developer account (unsigned .dmg + Gatekeeper procedure); command palette included; #40 not in the milestone; "tout d'un coup".
180+- **Changes**: Go — `internal/app/newagent.go` (`AgentForm`, `entryOf`, `splitArguments`, `ConfigService.AddAgent`, `ChooseFolder`, `Options.PickFolder`), `config.RuntimeEntry`, `unescapeLongCharacters` (yaml.v3 wrote 🤖 as `"\U0001F916"`), default config comment, `internal/shellpath` (interactive login shell with markers, merge, Apply; called in main.go before locating the config). Frontend — `lib/agentform.ts`, `lib/agent/AddAgentForm.svelte`, welcome screen (wireframe 6) in `ConversationPanel`, "+ Add an agent" in AgentsPanel, `lib/errors.ts` + `chat/ErrorText.svelte` used by ActionError/AgentsPanel/MessageView/AgentView/TerminalView (ActionError hides an error already shown on the agent row), `lib/shortcuts.ts`, `layout/CommandPalette.svelte`, Shell keydown (terminal keeps Ctrl outside macOS), `lib/contrast.ts` + test (style.css read via `?raw`, `test.css.include` in vite.config.ts), light theme colours darkened for AA, `:focus-visible`. Packaging — version 0.1.0 + `update:build-assets`, `build/linux/desktop` used instead of the generated one, nfpm licence/homepage/maintainer/icons, `scripts/package.sh` + test, `make package-*`, `.github/workflows/release.yml`, `.gitignore` (dist/, build/ios/, generated desktop). Diagram: `internal/shellpath` column.
181+- **Decisions**: errors stay technical in Go and are explained in the interface (unknown ones shown as they are); Rickub has Linux x86-64 runners only → CI builds Linux amd64 + Windows (CGO off cross-build), macOS on the Mac; tag must equal `v`+`build/config.yml` version.
182+- **Tests**: Go (entryOf, splitArguments, AddAgent, ChooseFolder, AppendAgent runtime/emoji, shellpath), vitest 85 (agentform, errors, shortcuts, contrast), shell (package names). `make check` green. Headless checks under Xvfb: welcome, assistant end to end (agent written, folder created, conversation opens), error sentence, palette, ⌘N/⌘L; AppImage launches; deb/rpm metadata checked; Windows installer built.
183+- **Quality**: golangci-lint 0 issues (qlty unavailable, #38).
184+- **Docs** (EN/FR): tutorial your-first-agent / premier-agent; how-to install-sidekicks, add-an-agent, publish-a-release, run-the-end-to-end-scenarios (+ report); reference keyboard-shortcuts, configuration (what the assistant writes), commands (package targets, package.sh, workflows, CI description fixed); explanation architecture (shell PATH, errors); indexes; root README (status, install, layout).
185+- **Follow-up (owner tested the assistant: works)**: copy a whole agent answer — `answerText` in `frontend/src/lib/conversation.ts` (text parts as Markdown, thoughts/tools/plans left out, tested), a "⧉ Copy" button next to the agent's name, shown on hover/focus once the answer is done (`MessageView.svelte`, key `message.copy`). Tutorial first-conversation (EN/FR) mentions it. Not seen on screen in the sandbox (no real agent there).
186+- **Follow-up**: `make package-macos` on the Mac warned `ld: object file … built for newer 'macOS' version (13.0) than being linked (12.0)`: the Go toolchain targets macOS 13 while Wails' darwin Taskfile asked for 12.0. Minimum set to 13.0 (Taskfile CGO flags + MACOSX_DEPLOYMENT_TARGET, LSMinimumSystemVersion 13.0.0 in both plists); install docs say macOS 13+; publish docs warn that `update:build-assets` resets the plists to 12.0.0.
187+- **Follow-up (owner pointed at rickub/cli)**: its `RELEASING.md` and `release.yml` show what works on Rickub's CI: `workflow_dispatch` (the only dispatchable workflow; the web UI targets one), `permissions: contents: write` (the token is read-only otherwise), `softprops/action-gh-release@v2` through the `/gh` shim behind `$GITHUB_API_URL` (creates the tag at `target_commitish`), artifacts uploaded first (fallback when publishing fails with 403/422). `.github/workflows/release.yml` rewritten on that model: version from `build/config.yml`, tag-exists check, `dry_run`, notes with checksums. Docs publish-a-release and commands reference (EN/FR) follow. Rickub's release page has no upload button: how the macOS packages reach a release is open.
188+- **Follow-up (macOS packages into the release)**: Rickub's CLI cannot upload release files (no `release` command; `rickub api` sends JSON only; the public API has no asset endpoint); the release web page accepts files by hand (owner checked). Owner chose the branch route: `scripts/push-macos-packages.sh` (on the Mac, after `make package-macos`: the .dmg and .zip as one parentless commit `built-from: <HEAD>`, built with plumbing — hash-object, mktree, commit-tree — force-pushed to `macos-packages`), `scripts/fetch-macos-packages.sh` (CI: writes them into dist/ for the version of build/config.yml, fails listing the branch content otherwise, prints built-from). Release workflow: step "macOS packages" before the checksums, input `without_macos`, warning when built-from ≠ released commit, notes say where the macOS packages come from. Test `scripts/macos-packages_test.sh` (bare remote + Mac clone + CI clone, 9 checks) in `make test-scripts`. Docs publish-a-release (steps reordered: version → Mac packages → workflow), commands reference, run-the-tests (EN/FR).
189+- **Follow-up**: root `RELEASING.md` / `RELEASING.fr.md` (owner asked for a document at the root): how a release is built (table), five-step checklist (version → Mac packages → dry run → publish → after), troubleshooting table, manual builds; linked from README and the publish-a-release guides.
190+- **Follow-up (owner: "c'est trop compliqué")**: release procedure simplified to the owner's plan — build everything on the Mac, tag with a script, the tag push creates the release, the files are attached by hand. `scripts/build-release.sh` (`make release-build`: macOS native, Windows cross-built, Linux `scripts/package.sh linux` in a Docker container from `scripts/linux-packages.Dockerfile` per `LINUX_ARCHES` (default amd64), `SKIP`, then `dist/SHA256SUMS`), `scripts/tag-release.sh` (`make release-tag`: clean tree, on main, equal to origin/main, tag free; warns when dist/ misses a package; annotated tag pushed; `--no-push`; test `tag-release_test.sh`, 11 checks), `.github/workflows/release.yml` now `on: push: tags: v*` → checks tag = config version, creates the release with notes (file table) via softprops, no files, `contents: write`. The `macos-packages` branch scripts and their test were removed; CI no longer builds packages. `scripts/package.sh` removes `bin/sidekicks` before a Linux or macOS build (a binary of the other OS in the shared bin/ made go build fail: "not an object file"). The Docker image needs `ibus-gtk4` (the AppImage GTK plugin copies `/usr/lib/<arch>/gtk-4.0`). RELEASING.md/.fr.md, publish-a-release (EN/FR) and the commands reference rewritten.
@@ -160,3 +160,31 @@
160 - **CI follow-up**: `node: command not found` — Rickub's runner runs JavaScript actions inside the job container; `nodejs` added to the first (bash-only) step.160 - **CI follow-up**: `node: command not found` — Rickub's runner runs JavaScript actions inside the job container; `nodejs` added to the first (bash-only) step.
161 - **CI follow-up 2**: `actions/setup-go` on Rickub ignored `go-version-file` and found no Go. setup-go and setup-node removed: Go installed from go.dev (version from go.mod, dpkg arch), node and npm from Ubuntu 26.04 (node 22.22); only actions/checkout kept.161 - **CI follow-up 2**: `actions/setup-go` on Rickub ignored `go-version-file` and found no Go. setup-go and setup-node removed: Go installed from go.dev (version from go.mod, dpkg arch), node and npm from Ubuntu 26.04 (node 22.22); only actions/checkout kept.
162 - **CI follow-up 3**: lint, Go tests, svelte-check and vitest pass on Rickub; the shell tests failed for want of `jq` (scripts/agents.sh) — jq added to the apt packages.162 - **CI follow-up 3**: lint, Go tests, svelte-check and vitest pass on Rickub; the shell tests failed for want of `jq` (scripts/agents.sh) — jq added to the apt packages.
163+
164+## 2026-10-30 — Logo
165+
166+- **Goal**: owner asked for logo proposals while the CI ran; five concepts drawn on a private design canvas (Duo, Orbite, La bande, Le compagnon, S en deux voix); owner chose **02 · Orbite** (centre dot = the user, teal and coral dots in orbit = the agents).
167+- **Changes**: `docs/design/logo/` (mark light/dark, favicon without the ring, app icon copy); `build/appicon.svg` (new source: 1024 canvas, 824 ink tile, macOS grid) → `build/appicon.png`, `build/darwin/icons.icns`, `build/windows/icon.ico` regenerated with `wails3 generate icons`; `build/appicon.icon` glyph replaced (`Assets/sidekicks-mark.svg`, ink automatic-gradient fill, Wails default glyph removed); README shows the mark (light/dark `<picture>`); `docs/design/README.md` Logo section with the regeneration commands.
168+- **Not done here**: `build/darwin/Assets.car` is still the Wails one — it can only be rebuilt on macOS (`-iconcomposerinput appicon.icon -macassetdir darwin`); `dmg-file-icon.*` untouched.
169+- **Follow-up**: owner regenerated `build/darwin/Assets.car` on the Mac. The welcome screen (no channel open, i.e. at launch) now shows the mark above "Sidekicks": `frontend/src/lib/layout/Logo.svelte` (inline SVG, `$props.id()` for the mask id), colours from new `--logo-*` tokens in `style.css` (light and dark). svelte-check, vitest and licence headers pass.
170+- **Follow-up 2**: owner wants the graphics in `docs/design/` in English. `wireframes.py` texts translated (window, messages, notes, titles; the sample `AGENTS.md` stays a French-typography style guide), the six SVG/PNG and `wireframes.drawio` regenerated, `docs/design/README.md` screen names and language sentence updated.
171+
172+## 2026-10-30 — Session closed
173+
174+- **CI**: owner confirms the run is green on Rickub after the `jq` fix (container ubuntu:26.04, Go from go.dev, node/npm/jq from apt).
175+- **Logo work** (above) checked locally: `make check` passes (golangci-lint, Go tests, svelte-check, vitest 63, agents.sh 15, licence headers).
176+
177+## 2026-10-30 — v0.1 · Première release (implementation)
178+
179+- **Goal**: milestone v0.1 (#33 onboarding, #34 end-to-end scenarios, #35 keyboard and accessibility, #36 macOS packaging) plus, asked mid-way, Linux and Windows packages and a workflow that builds them and attaches them to a release. Owner's choices: assistant for local + Docker + sbx; scenarios as a step-by-step guide + report; no Apple Developer account (unsigned .dmg + Gatekeeper procedure); command palette included; #40 not in the milestone; "tout d'un coup".
180+- **Changes**: Go — `internal/app/newagent.go` (`AgentForm`, `entryOf`, `splitArguments`, `ConfigService.AddAgent`, `ChooseFolder`, `Options.PickFolder`), `config.RuntimeEntry`, `unescapeLongCharacters` (yaml.v3 wrote 🤖 as `"\U0001F916"`), default config comment, `internal/shellpath` (interactive login shell with markers, merge, Apply; called in main.go before locating the config). Frontend — `lib/agentform.ts`, `lib/agent/AddAgentForm.svelte`, welcome screen (wireframe 6) in `ConversationPanel`, "+ Add an agent" in AgentsPanel, `lib/errors.ts` + `chat/ErrorText.svelte` used by ActionError/AgentsPanel/MessageView/AgentView/TerminalView (ActionError hides an error already shown on the agent row), `lib/shortcuts.ts`, `layout/CommandPalette.svelte`, Shell keydown (terminal keeps Ctrl outside macOS), `lib/contrast.ts` + test (style.css read via `?raw`, `test.css.include` in vite.config.ts), light theme colours darkened for AA, `:focus-visible`. Packaging — version 0.1.0 + `update:build-assets`, `build/linux/desktop` used instead of the generated one, nfpm licence/homepage/maintainer/icons, `scripts/package.sh` + test, `make package-*`, `.github/workflows/release.yml`, `.gitignore` (dist/, build/ios/, generated desktop). Diagram: `internal/shellpath` column.
181+- **Decisions**: errors stay technical in Go and are explained in the interface (unknown ones shown as they are); Rickub has Linux x86-64 runners only → CI builds Linux amd64 + Windows (CGO off cross-build), macOS on the Mac; tag must equal `v`+`build/config.yml` version.
182+- **Tests**: Go (entryOf, splitArguments, AddAgent, ChooseFolder, AppendAgent runtime/emoji, shellpath), vitest 85 (agentform, errors, shortcuts, contrast), shell (package names). `make check` green. Headless checks under Xvfb: welcome, assistant end to end (agent written, folder created, conversation opens), error sentence, palette, ⌘N/⌘L; AppImage launches; deb/rpm metadata checked; Windows installer built.
183+- **Quality**: golangci-lint 0 issues (qlty unavailable, #38).
184+- **Docs** (EN/FR): tutorial your-first-agent / premier-agent; how-to install-sidekicks, add-an-agent, publish-a-release, run-the-end-to-end-scenarios (+ report); reference keyboard-shortcuts, configuration (what the assistant writes), commands (package targets, package.sh, workflows, CI description fixed); explanation architecture (shell PATH, errors); indexes; root README (status, install, layout).
185+- **Follow-up (owner tested the assistant: works)**: copy a whole agent answer — `answerText` in `frontend/src/lib/conversation.ts` (text parts as Markdown, thoughts/tools/plans left out, tested), a "⧉ Copy" button next to the agent's name, shown on hover/focus once the answer is done (`MessageView.svelte`, key `message.copy`). Tutorial first-conversation (EN/FR) mentions it. Not seen on screen in the sandbox (no real agent there).
186+- **Follow-up**: `make package-macos` on the Mac warned `ld: object file … built for newer 'macOS' version (13.0) than being linked (12.0)`: the Go toolchain targets macOS 13 while Wails' darwin Taskfile asked for 12.0. Minimum set to 13.0 (Taskfile CGO flags + MACOSX_DEPLOYMENT_TARGET, LSMinimumSystemVersion 13.0.0 in both plists); install docs say macOS 13+; publish docs warn that `update:build-assets` resets the plists to 12.0.0.
187+- **Follow-up (owner pointed at rickub/cli)**: its `RELEASING.md` and `release.yml` show what works on Rickub's CI: `workflow_dispatch` (the only dispatchable workflow; the web UI targets one), `permissions: contents: write` (the token is read-only otherwise), `softprops/action-gh-release@v2` through the `/gh` shim behind `$GITHUB_API_URL` (creates the tag at `target_commitish`), artifacts uploaded first (fallback when publishing fails with 403/422). `.github/workflows/release.yml` rewritten on that model: version from `build/config.yml`, tag-exists check, `dry_run`, notes with checksums. Docs publish-a-release and commands reference (EN/FR) follow. Rickub's release page has no upload button: how the macOS packages reach a release is open.
188+- **Follow-up (macOS packages into the release)**: Rickub's CLI cannot upload release files (no `release` command; `rickub api` sends JSON only; the public API has no asset endpoint); the release web page accepts files by hand (owner checked). Owner chose the branch route: `scripts/push-macos-packages.sh` (on the Mac, after `make package-macos`: the .dmg and .zip as one parentless commit `built-from: <HEAD>`, built with plumbing — hash-object, mktree, commit-tree — force-pushed to `macos-packages`), `scripts/fetch-macos-packages.sh` (CI: writes them into dist/ for the version of build/config.yml, fails listing the branch content otherwise, prints built-from). Release workflow: step "macOS packages" before the checksums, input `without_macos`, warning when built-from ≠ released commit, notes say where the macOS packages come from. Test `scripts/macos-packages_test.sh` (bare remote + Mac clone + CI clone, 9 checks) in `make test-scripts`. Docs publish-a-release (steps reordered: version → Mac packages → workflow), commands reference, run-the-tests (EN/FR).
189+- **Follow-up**: root `RELEASING.md` / `RELEASING.fr.md` (owner asked for a document at the root): how a release is built (table), five-step checklist (version → Mac packages → dry run → publish → after), troubleshooting table, manual builds; linked from README and the publish-a-release guides.
190+- **Follow-up (owner: "c'est trop compliqué")**: release procedure simplified to the owner's plan — build everything on the Mac, tag with a script, the tag push creates the release, the files are attached by hand. `scripts/build-release.sh` (`make release-build`: macOS native, Windows cross-built, Linux `scripts/package.sh linux` in a Docker container from `scripts/linux-packages.Dockerfile` per `LINUX_ARCHES` (default amd64), `SKIP`, then `dist/SHA256SUMS`), `scripts/tag-release.sh` (`make release-tag`: clean tree, on main, equal to origin/main, tag free; warns when dist/ misses a package; annotated tag pushed; `--no-push`; test `tag-release_test.sh`, 11 checks), `.github/workflows/release.yml` now `on: push: tags: v*` → checks tag = config version, creates the release with notes (file table) via softprops, no files, `contents: write`. The `macos-packages` branch scripts and their test were removed; CI no longer builds packages. `scripts/package.sh` removes `bin/sidekicks` before a Linux or macOS build (a binary of the other OS in the shared bin/ made go build fail: "not an object file"). The Docker image needs `ibus-gtk4` (the AppImage GTK plugin copies `/usr/lib/<arch>/gtk-4.0`). RELEASING.md/.fr.md, publish-a-release (EN/FR) and the commands reference rewritten.
modified .memory/summary.md +11 -6
@@ -4,15 +4,18 @@
44
55 A desktop application (Go + Wails) to talk with AI agents in channels, for non-technical ("citizen") users. Agent-agnostic: every agent is reached over the Agent Client Protocol (ACP, JSON-RPC on stdio). Full brief: `.tickets/issues/0001-draft.yaml`; backlog of 36 issues, 9 epics, 6 milestones in `.tickets/` (IssueSpec format, see `.claude/skills/issuespec`).
66
7-## State (2026-10-30, M4 closed)
7+## State (2026-10-30, v0.1 in progress)
88
9-Milestones **Initialize**, **M1**, **M2**, **M3** and **M4 · Workspace des agents** are closed (M4 validated on the Mac, branch `feature/m4-agents-workspace`; #32 in the backlog). Workspaces: local, Docker container and sbx sandbox (`workspace.Remote`, verified on `milo-sbx`). Test agents are started with mm directly, with the optional `afterStop` unloading their model. Agent view in the centre (📂 on an agent row): Files (lazy tree, hidden files toggle, CodeMirror editor with conflict detection, create, delete to trash), Terminal (xterm.js + pty, local or docker exec), Log. M3 recap: Channels persisted in Pebble (one DB per config file), direct conversations = channels with one member, `@` mentions with popup + invitation, sequential multi-agent answers with unseen-context injection, `/` and `/agent:command` popup (commands cached in the store), resume after restart by context re-injection (mm has no session/load), channel CRUD in the Channels panel, unread badges. Since M2: streaming Markdown (Shiki, DOMPurify), tool calls with diffs, permission cards in plain words, stuck-agent banner, Stop, message queue, context gauge; Settings editor (CodeMirror, live validation by Go, schema completion, save/revert); open another config file + recent list + last-opened at startup. The interface is in **English by default**, French with `ui: language: fr` (ADR 0005). Next: v0.1 · Première release (#33–#37), #38 qlty kit.
9+**v0.1 · Première release** (#33–#37) is implemented on branch `feature/1st-release`, waiting for the owner's validation on the Mac (end-to-end scenarios guide, section 7 for v0.1 checks): welcome screen + "Add an agent" assistant (`ConfigService.AddAgent`/`ChooseFolder`, local/container/sbx), human error messages (`frontend/src/lib/errors.ts` + `ErrorText`), shortcuts + ⌘K palette (`lib/shortcuts.ts`, `CommandPalette.svelte`), WCAG contrast test, `:focus-visible`, `internal/shellpath` (shell PATH at startup), version 0.1.0, packages for macOS/Linux/Windows (`scripts/package.sh`, `make package-*`, `dist/`), release workflow (`.github/workflows/release.yml`). Logo "Orbite" (`docs/design/logo/`, `build/appicon.svg`).
10+
11+
12+Milestones **Initialize**, **M1**, **M2**, **M3** and **M4 · Workspace des agents** are closed (M4 validated on the Mac, branch `feature/m4-agents-workspace`; #32 in the backlog). Workspaces: local, Docker container and sbx sandbox (`workspace.Remote`, verified on `milo-sbx`). Test agents are started with mm directly, with the optional `afterStop` unloading their model. Agent view in the centre (📂 on an agent row): Files (lazy tree, hidden files toggle, CodeMirror editor with conflict detection, create, delete to trash), Terminal (xterm.js + pty, local or docker exec), Log. M3 recap: Channels persisted in Pebble (one DB per config file), direct conversations = channels with one member, `@` mentions with popup + invitation, sequential multi-agent answers with unseen-context injection, `/` and `/agent:command` popup (commands cached in the store), resume after restart by context re-injection (mm has no session/load), channel CRUD in the Channels panel, unread badges. Since M2: streaming Markdown (Shiki, DOMPurify), tool calls with diffs, permission cards in plain words, stuck-agent banner, Stop, message queue, context gauge; Settings editor (CodeMirror, live validation by Go, schema completion, save/revert); open another config file + recent list + last-opened at startup. The interface is in **English by default**, French with `ui: language: fr` (ADR 0005). Next after v0.1: #38 qlty kit, #40 sbx create/remove hook, #32.
1013
1114 What exists:
1215
13-- `main.go` (root, only Wails importer; registers 11 typed events; injects the native file dialog; its emitter ignores events sent before the Wails app exists) · `internal/app` (Core, AgentService, ChannelService, ConfigService; opens the history per config file) · `internal/channels` (routing, sessions, context, queue, permissions) · `internal/store` (Pebble) · `internal/workspace` (Local, Container, Terminals; contract tests local + alpine container) · `internal/agents` · `internal/acp` (sessions, readable AgentError, stdin→TERM→KILL) · `internal/config` (+ `WatchWithReady`) · `internal/prefs` · `internal/events` · `schema`.
16+- `main.go` (root, only Wails importer; registers 11 typed events; injects the native file dialog; its emitter ignores events sent before the Wails app exists) · `internal/app` (Core, AgentService, ChannelService, ConfigService; opens the history per config file) · `internal/channels` (routing, sessions, context, queue, permissions) · `internal/store` (Pebble) · `internal/workspace` (Local, Container, Terminals; contract tests local + alpine container) · `internal/agents` · `internal/acp` (sessions, readable AgentError, stdin→TERM→KILL) · `internal/config` (+ `WatchWithReady`, `AppendAgent` keeps comments and unescapes emoji) · `internal/prefs` · `internal/events` · `internal/shellpath` · `schema`.
1417 - `frontend/`: shell + panel registry, `state.svelte.ts` (`appState`, one ordered frame batcher for message events), `lib/chat/*` (Markdown, MessageView, ToolCallCard, PermissionCard, Composer), `lib/settings/ConfigEditor.svelte`, `lib/i18n` (en/fr catalogue, `t()`), pure modules with vitest (`markdown`, `conversation`, `composer`, `suggestions`, `tools`, `yamlSchema`, `events`, `theme`, `panels/registry`). State is keyed by channel id; a `$derived` must never call `conversationOf` (it writes state → `state_unsafe_mutation` freezes the UI).
15-- Tooling: `Makefile`, golangci-lint (`tools/go.mod`), CI, `scripts/agents.sh`, `scripts/install-test-config.sh`, `scripts/check-license-headers.sh`.
18+- Tooling: `Makefile`, golangci-lint (`tools/go.mod`), CI (`.github/workflows/ci.yml`, job in a `ubuntu:26.04` container, green on Rickub), `scripts/agents.sh`, `scripts/install-test-config.sh`, `scripts/check-license-headers.sh`, `scripts/package.sh` (+ `package_test.sh`), `scripts/build-release.sh`, `scripts/tag-release.sh` (+ `tag-release_test.sh`), `scripts/linux-packages.Dockerfile`, release workflow (tag push → release) `.github/workflows/release.yml`.
1619
1720 ## Decisions in force
1821
@@ -37,10 +40,11 @@ What exists:
3740 | Everything the CI runs | `make check` (lint + tests; needs `wails3`, `pnpm`, GTK/WebKit dev packages on Linux) |
3841 | Tests only | `make test` (Go + vitest + shell) · real agents: `make test-real` |
3942 | Run | `SIDEKICKS_CONFIG="$PWD/examples/test-agents.yaml" make dev` · build `make build` · macOS app `make package` |
43+| Release | `make release-build` (Mac: all packages → dist/, Linux in Docker) · `make release-tag` · single systems: `make package-linux` / `package-windows` / `package-macos`; see RELEASING.md |
4044 | Bindings | `make bindings` after changing a service/event; CI runs `make bindings-check` |
4145 | Test agents | `scripts/agents.sh run alice` · `ps` · `unload --all` (`MM_BIN`, llmman on 127.0.0.1 or host.docker.internal:17434) |
4246 | Headless UI check (sandbox) | `Xvfb :97`, `GDK_BACKEND=x11 WEBKIT_DISABLE_DMABUF_RENDERER=1 dbus-launch bin/sidekicks --config …`, `xdotool`, `import -window root` |
43-| Wireframes / package diagram | `python3 docs/design/wireframes.py` · `python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams` + `rsvg-convert -w 1200 -h 800` |
47+| Wireframes / package diagram | `python3 docs/design/wireframes.py` · `python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams` + `rsvg-convert -w 1690 -h 800` |
4448
4549 ## Facts about the test agents (mm v1.0.3)
4650
@@ -54,6 +58,7 @@ What exists:
5458
5559 - How to exec a shell / read files inside an `sbx` sandbox (issue #28).
5660 - Quality gate: `qlty` is not in this sandbox → no qlty measurement yet (issue #38: build a sandbox kit with qlty). golangci-lint (go tool) is the stopgap.
57-- Whether the CI workflow runs as-is on rickub.
5861 - On the shared Mac⇄Linux folder, the `mkdir` lock of `scripts/agents.sh` is not exclusive: two agents starting at the same instant could exceed the slot limit in the sandbox. Not seen; on the Mac's own disk mkdir is atomic.
62+- Release procedure (RELEASING.md): `make release-build` on the Mac (everything into dist/, Linux in Docker), `make release-tag` (pushes `v<version>`), the tag push makes `.github/workflows/release.yml` create the release (notes only, softprops), files attached by hand on the release page. The workflow has not run on Rickub yet; `make release-build` has not run on the Mac yet (Linux arm64 in Docker checked in the sandbox).
63+- `make package-macos` (universal .dmg) and the Windows installer have not been run on their systems; the folder dialog of the assistant (`ChooseFolder`) not tried on the Mac.
5964 - Whether `.claude/skills/*` (kit files) should be committed with the project is the owner's call.
@@ -4,15 +4,18 @@
4 4
5 A desktop application (Go + Wails) to talk with AI agents in channels, for non-technical ("citizen") users. Agent-agnostic: every agent is reached over the Agent Client Protocol (ACP, JSON-RPC on stdio). Full brief: `.tickets/issues/0001-draft.yaml`; backlog of 36 issues, 9 epics, 6 milestones in `.tickets/` (IssueSpec format, see `.claude/skills/issuespec`).5 A desktop application (Go + Wails) to talk with AI agents in channels, for non-technical ("citizen") users. Agent-agnostic: every agent is reached over the Agent Client Protocol (ACP, JSON-RPC on stdio). Full brief: `.tickets/issues/0001-draft.yaml`; backlog of 36 issues, 9 epics, 6 milestones in `.tickets/` (IssueSpec format, see `.claude/skills/issuespec`).
6 6
7-## State (2026-10-30, M4 closed)7+## State (2026-10-30, v0.1 in progress)
8 8
9-Milestones **Initialize**, **M1**, **M2**, **M3** and **M4 · Workspace des agents** are closed (M4 validated on the Mac, branch `feature/m4-agents-workspace`; #32 in the backlog). Workspaces: local, Docker container and sbx sandbox (`workspace.Remote`, verified on `milo-sbx`). Test agents are started with mm directly, with the optional `afterStop` unloading their model. Agent view in the centre (📂 on an agent row): Files (lazy tree, hidden files toggle, CodeMirror editor with conflict detection, create, delete to trash), Terminal (xterm.js + pty, local or docker exec), Log. M3 recap: Channels persisted in Pebble (one DB per config file), direct conversations = channels with one member, `@` mentions with popup + invitation, sequential multi-agent answers with unseen-context injection, `/` and `/agent:command` popup (commands cached in the store), resume after restart by context re-injection (mm has no session/load), channel CRUD in the Channels panel, unread badges. Since M2: streaming Markdown (Shiki, DOMPurify), tool calls with diffs, permission cards in plain words, stuck-agent banner, Stop, message queue, context gauge; Settings editor (CodeMirror, live validation by Go, schema completion, save/revert); open another config file + recent list + last-opened at startup. The interface is in **English by default**, French with `ui: language: fr` (ADR 0005). Next: v0.1 · Première release (#33–#37), #38 qlty kit.9+**v0.1 · Première release** (#33–#37) is implemented on branch `feature/1st-release`, waiting for the owner's validation on the Mac (end-to-end scenarios guide, section 7 for v0.1 checks): welcome screen + "Add an agent" assistant (`ConfigService.AddAgent`/`ChooseFolder`, local/container/sbx), human error messages (`frontend/src/lib/errors.ts` + `ErrorText`), shortcuts + ⌘K palette (`lib/shortcuts.ts`, `CommandPalette.svelte`), WCAG contrast test, `:focus-visible`, `internal/shellpath` (shell PATH at startup), version 0.1.0, packages for macOS/Linux/Windows (`scripts/package.sh`, `make package-*`, `dist/`), release workflow (`.github/workflows/release.yml`). Logo "Orbite" (`docs/design/logo/`, `build/appicon.svg`).
10+
11+
12+Milestones **Initialize**, **M1**, **M2**, **M3** and **M4 · Workspace des agents** are closed (M4 validated on the Mac, branch `feature/m4-agents-workspace`; #32 in the backlog). Workspaces: local, Docker container and sbx sandbox (`workspace.Remote`, verified on `milo-sbx`). Test agents are started with mm directly, with the optional `afterStop` unloading their model. Agent view in the centre (📂 on an agent row): Files (lazy tree, hidden files toggle, CodeMirror editor with conflict detection, create, delete to trash), Terminal (xterm.js + pty, local or docker exec), Log. M3 recap: Channels persisted in Pebble (one DB per config file), direct conversations = channels with one member, `@` mentions with popup + invitation, sequential multi-agent answers with unseen-context injection, `/` and `/agent:command` popup (commands cached in the store), resume after restart by context re-injection (mm has no session/load), channel CRUD in the Channels panel, unread badges. Since M2: streaming Markdown (Shiki, DOMPurify), tool calls with diffs, permission cards in plain words, stuck-agent banner, Stop, message queue, context gauge; Settings editor (CodeMirror, live validation by Go, schema completion, save/revert); open another config file + recent list + last-opened at startup. The interface is in **English by default**, French with `ui: language: fr` (ADR 0005). Next after v0.1: #38 qlty kit, #40 sbx create/remove hook, #32.
10 13
11 What exists:14 What exists:
12 15
13-- `main.go` (root, only Wails importer; registers 11 typed events; injects the native file dialog; its emitter ignores events sent before the Wails app exists) · `internal/app` (Core, AgentService, ChannelService, ConfigService; opens the history per config file) · `internal/channels` (routing, sessions, context, queue, permissions) · `internal/store` (Pebble) · `internal/workspace` (Local, Container, Terminals; contract tests local + alpine container) · `internal/agents` · `internal/acp` (sessions, readable AgentError, stdin→TERM→KILL) · `internal/config` (+ `WatchWithReady`) · `internal/prefs` · `internal/events` · `schema`.16+- `main.go` (root, only Wails importer; registers 11 typed events; injects the native file dialog; its emitter ignores events sent before the Wails app exists) · `internal/app` (Core, AgentService, ChannelService, ConfigService; opens the history per config file) · `internal/channels` (routing, sessions, context, queue, permissions) · `internal/store` (Pebble) · `internal/workspace` (Local, Container, Terminals; contract tests local + alpine container) · `internal/agents` · `internal/acp` (sessions, readable AgentError, stdin→TERM→KILL) · `internal/config` (+ `WatchWithReady`, `AppendAgent` keeps comments and unescapes emoji) · `internal/prefs` · `internal/events` · `internal/shellpath` · `schema`.
14 - `frontend/`: shell + panel registry, `state.svelte.ts` (`appState`, one ordered frame batcher for message events), `lib/chat/*` (Markdown, MessageView, ToolCallCard, PermissionCard, Composer), `lib/settings/ConfigEditor.svelte`, `lib/i18n` (en/fr catalogue, `t()`), pure modules with vitest (`markdown`, `conversation`, `composer`, `suggestions`, `tools`, `yamlSchema`, `events`, `theme`, `panels/registry`). State is keyed by channel id; a `$derived` must never call `conversationOf` (it writes state → `state_unsafe_mutation` freezes the UI).17 - `frontend/`: shell + panel registry, `state.svelte.ts` (`appState`, one ordered frame batcher for message events), `lib/chat/*` (Markdown, MessageView, ToolCallCard, PermissionCard, Composer), `lib/settings/ConfigEditor.svelte`, `lib/i18n` (en/fr catalogue, `t()`), pure modules with vitest (`markdown`, `conversation`, `composer`, `suggestions`, `tools`, `yamlSchema`, `events`, `theme`, `panels/registry`). State is keyed by channel id; a `$derived` must never call `conversationOf` (it writes state → `state_unsafe_mutation` freezes the UI).
15-- Tooling: `Makefile`, golangci-lint (`tools/go.mod`), CI, `scripts/agents.sh`, `scripts/install-test-config.sh`, `scripts/check-license-headers.sh`.18+- Tooling: `Makefile`, golangci-lint (`tools/go.mod`), CI (`.github/workflows/ci.yml`, job in a `ubuntu:26.04` container, green on Rickub), `scripts/agents.sh`, `scripts/install-test-config.sh`, `scripts/check-license-headers.sh`, `scripts/package.sh` (+ `package_test.sh`), `scripts/build-release.sh`, `scripts/tag-release.sh` (+ `tag-release_test.sh`), `scripts/linux-packages.Dockerfile`, release workflow (tag push → release) `.github/workflows/release.yml`.
16 19
17 ## Decisions in force20 ## Decisions in force
18 21
@@ -37,10 +40,11 @@ What exists:
37 | Everything the CI runs | `make check` (lint + tests; needs `wails3`, `pnpm`, GTK/WebKit dev packages on Linux) |40 | Everything the CI runs | `make check` (lint + tests; needs `wails3`, `pnpm`, GTK/WebKit dev packages on Linux) |
38 | Tests only | `make test` (Go + vitest + shell) · real agents: `make test-real` |41 | Tests only | `make test` (Go + vitest + shell) · real agents: `make test-real` |
39 | Run | `SIDEKICKS_CONFIG="$PWD/examples/test-agents.yaml" make dev` · build `make build` · macOS app `make package` |42 | Run | `SIDEKICKS_CONFIG="$PWD/examples/test-agents.yaml" make dev` · build `make build` · macOS app `make package` |
43+| Release | `make release-build` (Mac: all packages → dist/, Linux in Docker) · `make release-tag` · single systems: `make package-linux` / `package-windows` / `package-macos`; see RELEASING.md |
40 | Bindings | `make bindings` after changing a service/event; CI runs `make bindings-check` |44 | Bindings | `make bindings` after changing a service/event; CI runs `make bindings-check` |
41 | Test agents | `scripts/agents.sh run alice` · `ps` · `unload --all` (`MM_BIN`, llmman on 127.0.0.1 or host.docker.internal:17434) |45 | Test agents | `scripts/agents.sh run alice` · `ps` · `unload --all` (`MM_BIN`, llmman on 127.0.0.1 or host.docker.internal:17434) |
42 | Headless UI check (sandbox) | `Xvfb :97`, `GDK_BACKEND=x11 WEBKIT_DISABLE_DMABUF_RENDERER=1 dbus-launch bin/sidekicks --config …`, `xdotool`, `import -window root` |46 | Headless UI check (sandbox) | `Xvfb :97`, `GDK_BACKEND=x11 WEBKIT_DISABLE_DMABUF_RENDERER=1 dbus-launch bin/sidekicks --config …`, `xdotool`, `import -window root` |
43-| Wireframes / package diagram | `python3 docs/design/wireframes.py` · `python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams` + `rsvg-convert -w 1200 -h 800` |47+| Wireframes / package diagram | `python3 docs/design/wireframes.py` · `python3 .claude/skills/drawio-fancy-diagram/generator/examples/sidekicks_packages.py docs/diagrams` + `rsvg-convert -w 1690 -h 800` |
44 48
45 ## Facts about the test agents (mm v1.0.3)49 ## Facts about the test agents (mm v1.0.3)
46 50
@@ -54,6 +58,7 @@ What exists:
54 58
55 - How to exec a shell / read files inside an `sbx` sandbox (issue #28).59 - How to exec a shell / read files inside an `sbx` sandbox (issue #28).
56 - Quality gate: `qlty` is not in this sandbox → no qlty measurement yet (issue #38: build a sandbox kit with qlty). golangci-lint (go tool) is the stopgap.60 - Quality gate: `qlty` is not in this sandbox → no qlty measurement yet (issue #38: build a sandbox kit with qlty). golangci-lint (go tool) is the stopgap.
57-- Whether the CI workflow runs as-is on rickub.
58 - On the shared Mac⇄Linux folder, the `mkdir` lock of `scripts/agents.sh` is not exclusive: two agents starting at the same instant could exceed the slot limit in the sandbox. Not seen; on the Mac's own disk mkdir is atomic.61 - On the shared Mac⇄Linux folder, the `mkdir` lock of `scripts/agents.sh` is not exclusive: two agents starting at the same instant could exceed the slot limit in the sandbox. Not seen; on the Mac's own disk mkdir is atomic.
62+- Release procedure (RELEASING.md): `make release-build` on the Mac (everything into dist/, Linux in Docker), `make release-tag` (pushes `v<version>`), the tag push makes `.github/workflows/release.yml` create the release (notes only, softprops), files attached by hand on the release page. The workflow has not run on Rickub yet; `make release-build` has not run on the Mac yet (Linux arm64 in Docker checked in the sandbox).
63+- `make package-macos` (universal .dmg) and the Windows installer have not been run on their systems; the folder dialog of the assistant (`ChooseFolder`) not tried on the Mac.
59 - Whether `.claude/skills/*` (kit files) should be committed with the project is the owner's call.64 - Whether `.claude/skills/*` (kit files) should be committed with the project is the owner's call.
modified .tickets/issues/0033-premier-lancement-et-onboarding-du-citizen-user.yaml +29 -6
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-29T07:20:31.000Z
9-updatedAt: 2026-10-29T07:20:31.000Z
9+updatedAt: 2026-10-30T07:39:01.000Z
1010 labels:
1111 - "kind::feature"
1212 - "area::ui"
@@ -32,34 +32,57 @@ body: |
3232 tasks:
3333 - id: 1
3434 title: "États vides"
35- state: open
35+ state: closed
3636 priority: high
3737 author:
3838 name: k33g
3939 email: ph.charriere@gmail.com
4040 createdAt: 2026-10-29T07:20:31.000Z
41+ updatedAt: 2026-10-30T07:39:01.000Z
42+ closedAt: 2026-10-30T07:39:01.000Z
4143 - id: 2
4244 title: "Assistant premier agent"
43- state: open
45+ state: closed
4446 priority: high
4547 author:
4648 name: k33g
4749 email: ph.charriere@gmail.com
4850 createdAt: 2026-10-29T07:20:31.000Z
51+ updatedAt: 2026-10-30T07:39:01.000Z
52+ closedAt: 2026-10-30T07:39:01.000Z
4953 - id: 3
5054 title: "Revue de tous les messages d'erreur"
51- state: open
55+ state: closed
5256 priority: medium
5357 author:
5458 name: k33g
5559 email: ph.charriere@gmail.com
5660 createdAt: 2026-10-29T07:20:31.000Z
61+ updatedAt: 2026-10-30T07:39:01.000Z
62+ closedAt: 2026-10-30T07:39:01.000Z
5763 - id: 4
5864 title: "Channel d'accueil"
59- state: open
65+ state: closed
6066 priority: medium
6167 author:
6268 name: k33g
6369 email: ph.charriere@gmail.com
6470 createdAt: 2026-10-29T07:20:31.000Z
65-comments: []
71+ updatedAt: 2026-10-30T07:39:01.000Z
72+ closedAt: 2026-10-30T07:39:01.000Z
73+comments:
74+ - id: 1
75+ author:
76+ name: k33g
77+ email: ph.charriere@gmail.com
78+ createdAt: 2026-10-30T07:39:01.000Z
79+ body: |
80+ Livré sur la branche `feature/1st-release` (à valider sur le Mac, section 7 de [Dérouler les scénarios de bout en bout](docs/fr/how-to/derouler-les-scenarios-de-bout-en-bout.md)).
81+
82+ - **États vides** : l'écran d'accueil reprend la maquette 6 (logo, trois étapes, « Ajouter mon premier agent », « Ouvrir une configuration… », chemin du fichier) ; le panneau Agents a un bouton « + Ajouter un agent ».
83+ - **Assistant** : `ConfigService.AddAgent` (internal/app/newagent.go) écrit l'agent dans le YAML en gardant les commentaires (`config.AppendAgent`, emoji lisibles), crée son dossier, refuse sans rien écrire un résultat invalide ; local, conteneur Docker ou sandbox sbx ; `ChooseFolder` ouvre le sélecteur de dossier. La conversation directe s'ouvre une fois la configuration rechargée.
84+ - **Messages d'erreur** : `frontend/src/lib/errors.ts` traduit les erreurs courantes (programme introuvable ou non exécutable, Docker arrêté, conteneur ou sandbox absents, limite d'agents, serveur de modèles injoignable, agent muet ou arrêté, protocole…) en phrases qui disent quoi faire, détails techniques repliés.
85+ - **Channel d'accueil** : `#general` est déjà créé par la configuration par défaut ; inchangé.
86+ - En plus : `internal/shellpath` donne à l'application le PATH du shell (app ouverte depuis le Finder).
87+
88+ Docs : tutoriel « Votre premier agent, sans écrire de configuration », guide « Ajouter un agent », référence configuration (ce qu'écrit l'assistant), explication architecture (EN/FR).
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-29T07:20:31.000Z8 createdAt: 2026-10-29T07:20:31.000Z
9-updatedAt: 2026-10-29T07:20:31.000Z9+updatedAt: 2026-10-30T07:39:01.000Z
10 labels:10 labels:
11 - "kind::feature"11 - "kind::feature"
12 - "area::ui"12 - "area::ui"
@@ -32,34 +32,57 @@ body: |
32 tasks:32 tasks:
33 - id: 133 - id: 1
34 title: "États vides"34 title: "États vides"
35- state: open35+ state: closed
36 priority: high36 priority: high
37 author:37 author:
38 name: k33g38 name: k33g
39 email: ph.charriere@gmail.com39 email: ph.charriere@gmail.com
40 createdAt: 2026-10-29T07:20:31.000Z40 createdAt: 2026-10-29T07:20:31.000Z
41+ updatedAt: 2026-10-30T07:39:01.000Z
42+ closedAt: 2026-10-30T07:39:01.000Z
41 - id: 243 - id: 2
42 title: "Assistant premier agent"44 title: "Assistant premier agent"
43- state: open45+ state: closed
44 priority: high46 priority: high
45 author:47 author:
46 name: k33g48 name: k33g
47 email: ph.charriere@gmail.com49 email: ph.charriere@gmail.com
48 createdAt: 2026-10-29T07:20:31.000Z50 createdAt: 2026-10-29T07:20:31.000Z
51+ updatedAt: 2026-10-30T07:39:01.000Z
52+ closedAt: 2026-10-30T07:39:01.000Z
49 - id: 353 - id: 3
50 title: "Revue de tous les messages d'erreur"54 title: "Revue de tous les messages d'erreur"
51- state: open55+ state: closed
52 priority: medium56 priority: medium
53 author:57 author:
54 name: k33g58 name: k33g
55 email: ph.charriere@gmail.com59 email: ph.charriere@gmail.com
56 createdAt: 2026-10-29T07:20:31.000Z60 createdAt: 2026-10-29T07:20:31.000Z
61+ updatedAt: 2026-10-30T07:39:01.000Z
62+ closedAt: 2026-10-30T07:39:01.000Z
57 - id: 463 - id: 4
58 title: "Channel d'accueil"64 title: "Channel d'accueil"
59- state: open65+ state: closed
60 priority: medium66 priority: medium
61 author:67 author:
62 name: k33g68 name: k33g
63 email: ph.charriere@gmail.com69 email: ph.charriere@gmail.com
64 createdAt: 2026-10-29T07:20:31.000Z70 createdAt: 2026-10-29T07:20:31.000Z
65-comments: []71+ updatedAt: 2026-10-30T07:39:01.000Z
72+ closedAt: 2026-10-30T07:39:01.000Z
73+comments:
74+ - id: 1
75+ author:
76+ name: k33g
77+ email: ph.charriere@gmail.com
78+ createdAt: 2026-10-30T07:39:01.000Z
79+ body: |
80+ Livré sur la branche `feature/1st-release` (à valider sur le Mac, section 7 de [Dérouler les scénarios de bout en bout](docs/fr/how-to/derouler-les-scenarios-de-bout-en-bout.md)).
81+
82+ - **États vides** : l'écran d'accueil reprend la maquette 6 (logo, trois étapes, « Ajouter mon premier agent », « Ouvrir une configuration… », chemin du fichier) ; le panneau Agents a un bouton « + Ajouter un agent ».
83+ - **Assistant** : `ConfigService.AddAgent` (internal/app/newagent.go) écrit l'agent dans le YAML en gardant les commentaires (`config.AppendAgent`, emoji lisibles), crée son dossier, refuse sans rien écrire un résultat invalide ; local, conteneur Docker ou sandbox sbx ; `ChooseFolder` ouvre le sélecteur de dossier. La conversation directe s'ouvre une fois la configuration rechargée.
84+ - **Messages d'erreur** : `frontend/src/lib/errors.ts` traduit les erreurs courantes (programme introuvable ou non exécutable, Docker arrêté, conteneur ou sandbox absents, limite d'agents, serveur de modèles injoignable, agent muet ou arrêté, protocole…) en phrases qui disent quoi faire, détails techniques repliés.
85+ - **Channel d'accueil** : `#general` est déjà créé par la configuration par défaut ; inchangé.
86+ - En plus : `internal/shellpath` donne à l'application le PATH du shell (app ouverte depuis le Finder).
87+
88+ Docs : tutoriel « Votre premier agent, sans écrire de configuration », guide « Ajouter un agent », référence configuration (ce qu'écrit l'assistant), explication architecture (EN/FR).
modified .tickets/issues/0034-scenarios-de-test-de-bout-en-bout-avec-les-agent.yaml +11 -2
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-29T07:20:32.000Z
9-updatedAt: 2026-10-29T07:20:32.000Z
9+updatedAt: 2026-10-30T07:39:01.000Z
1010 labels:
1111 - "kind::chore"
1212 - "area::quality"
@@ -67,4 +67,13 @@ tasks:
6767 name: k33g
6868 email: ph.charriere@gmail.com
6969 createdAt: 2026-10-29T07:20:32.000Z
70-comments: []
70+comments:
71+ - id: 1
72+ author:
73+ name: k33g
74+ email: ph.charriere@gmail.com
75+ createdAt: 2026-10-30T07:39:01.000Z
76+ body: |
77+ Le guide pas à pas est prêt (EN/FR) : [Dérouler les scénarios de bout en bout](docs/fr/how-to/derouler-les-scenarios-de-bout-en-bout.md) — les six scénarios du ticket, déchargement des modèles entre chacun (`scripts/agents.sh unload --all`), les vérifications de la v0.1 (section 7) et un modèle de rapport à coller ici.
78+
79+ Reste à faire sur le Mac : dérouler les scénarios et poster le rapport (tâche 4).
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-29T07:20:32.000Z8 createdAt: 2026-10-29T07:20:32.000Z
9-updatedAt: 2026-10-29T07:20:32.000Z9+updatedAt: 2026-10-30T07:39:01.000Z
10 labels:10 labels:
11 - "kind::chore"11 - "kind::chore"
12 - "area::quality"12 - "area::quality"
@@ -67,4 +67,13 @@ tasks:
67 name: k33g67 name: k33g
68 email: ph.charriere@gmail.com68 email: ph.charriere@gmail.com
69 createdAt: 2026-10-29T07:20:32.000Z69 createdAt: 2026-10-29T07:20:32.000Z
70-comments: []70+comments:
71+ - id: 1
72+ author:
73+ name: k33g
74+ email: ph.charriere@gmail.com
75+ createdAt: 2026-10-30T07:39:01.000Z
76+ body: |
77+ Le guide pas à pas est prêt (EN/FR) : [Dérouler les scénarios de bout en bout](docs/fr/how-to/derouler-les-scenarios-de-bout-en-bout.md) — les six scénarios du ticket, déchargement des modèles entre chacun (`scripts/agents.sh unload --all`), les vérifications de la v0.1 (section 7) et un modèle de rapport à coller ici.
78+
79+ Reste à faire sur le Mac : dérouler les scénarios et poster le rapport (tâche 4).
modified .tickets/issues/0035-raccourcis-clavier-et-accessibilite.yaml +24 -5
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-29T07:20:33.000Z
9-updatedAt: 2026-10-29T07:20:33.000Z
9+updatedAt: 2026-10-30T07:39:01.000Z
1010 labels:
1111 - "kind::feature"
1212 - "area::ui"
@@ -26,26 +26,45 @@ body: |
2626 tasks:
2727 - id: 1
2828 title: "Raccourcis principaux"
29- state: open
29+ state: closed
3030 priority: medium
3131 author:
3232 name: k33g
3333 email: ph.charriere@gmail.com
3434 createdAt: 2026-10-29T07:20:33.000Z
35+ updatedAt: 2026-10-30T07:39:01.000Z
36+ closedAt: 2026-10-30T07:39:01.000Z
3537 - id: 2
3638 title: "Palette de commandes"
37- state: open
39+ state: closed
3840 priority: low
3941 author:
4042 name: k33g
4143 email: ph.charriere@gmail.com
4244 createdAt: 2026-10-29T07:20:33.000Z
45+ updatedAt: 2026-10-30T07:39:01.000Z
46+ closedAt: 2026-10-30T07:39:01.000Z
4347 - id: 3
4448 title: "Audit contrastes et navigation clavier"
45- state: open
49+ state: closed
4650 priority: medium
4751 author:
4852 name: k33g
4953 email: ph.charriere@gmail.com
5054 createdAt: 2026-10-29T07:20:33.000Z
51-comments: []
55+ updatedAt: 2026-10-30T07:39:01.000Z
56+ closedAt: 2026-10-30T07:39:01.000Z
57+comments:
58+ - id: 1
59+ author:
60+ name: k33g
61+ email: ph.charriere@gmail.com
62+ createdAt: 2026-10-30T07:39:01.000Z
63+ body: |
64+ Livré sur la branche `feature/1st-release` (à valider sur le Mac, vérifications 7.5 et 7.6 du guide des scénarios).
65+
66+ - **Raccourcis** (`frontend/src/lib/shortcuts.ts`, ⌘ sous macOS, Ctrl ailleurs) : ⌘K palette, ⌘N nouveau channel, ⇧⌘N ajouter un agent, ⌘L zone de message, ⌘B dock gauche, ⌘, Settings, ⌘J journal. Sous Linux/Windows, le terminal garde ses Ctrl.
67+ - **Palette de commandes** (`CommandPalette.svelte`) : channels, conversations et fichiers des agents, actions ; recherche floue, ↑↓ Entrée Échap, rôles ARIA combobox/listbox.
68+ - **Audit** : test de contraste WCAG AA (`lib/contrast.test.ts`) sur les couleurs du thème lues dans `style.css` ; quatre couleurs du thème clair assombries pour passer 4.5:1 ; `:focus-visible` partout.
69+
70+ Docs : référence « Raccourcis clavier » (EN/FR).
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-29T07:20:33.000Z8 createdAt: 2026-10-29T07:20:33.000Z
9-updatedAt: 2026-10-29T07:20:33.000Z9+updatedAt: 2026-10-30T07:39:01.000Z
10 labels:10 labels:
11 - "kind::feature"11 - "kind::feature"
12 - "area::ui"12 - "area::ui"
@@ -26,26 +26,45 @@ body: |
26 tasks:26 tasks:
27 - id: 127 - id: 1
28 title: "Raccourcis principaux"28 title: "Raccourcis principaux"
29- state: open29+ state: closed
30 priority: medium30 priority: medium
31 author:31 author:
32 name: k33g32 name: k33g
33 email: ph.charriere@gmail.com33 email: ph.charriere@gmail.com
34 createdAt: 2026-10-29T07:20:33.000Z34 createdAt: 2026-10-29T07:20:33.000Z
35+ updatedAt: 2026-10-30T07:39:01.000Z
36+ closedAt: 2026-10-30T07:39:01.000Z
35 - id: 237 - id: 2
36 title: "Palette de commandes"38 title: "Palette de commandes"
37- state: open39+ state: closed
38 priority: low40 priority: low
39 author:41 author:
40 name: k33g42 name: k33g
41 email: ph.charriere@gmail.com43 email: ph.charriere@gmail.com
42 createdAt: 2026-10-29T07:20:33.000Z44 createdAt: 2026-10-29T07:20:33.000Z
45+ updatedAt: 2026-10-30T07:39:01.000Z
46+ closedAt: 2026-10-30T07:39:01.000Z
43 - id: 347 - id: 3
44 title: "Audit contrastes et navigation clavier"48 title: "Audit contrastes et navigation clavier"
45- state: open49+ state: closed
46 priority: medium50 priority: medium
47 author:51 author:
48 name: k33g52 name: k33g
49 email: ph.charriere@gmail.com53 email: ph.charriere@gmail.com
50 createdAt: 2026-10-29T07:20:33.000Z54 createdAt: 2026-10-29T07:20:33.000Z
51-comments: []55+ updatedAt: 2026-10-30T07:39:01.000Z
56+ closedAt: 2026-10-30T07:39:01.000Z
57+comments:
58+ - id: 1
59+ author:
60+ name: k33g
61+ email: ph.charriere@gmail.com
62+ createdAt: 2026-10-30T07:39:01.000Z
63+ body: |
64+ Livré sur la branche `feature/1st-release` (à valider sur le Mac, vérifications 7.5 et 7.6 du guide des scénarios).
65+
66+ - **Raccourcis** (`frontend/src/lib/shortcuts.ts`, ⌘ sous macOS, Ctrl ailleurs) : ⌘K palette, ⌘N nouveau channel, ⇧⌘N ajouter un agent, ⌘L zone de message, ⌘B dock gauche, ⌘, Settings, ⌘J journal. Sous Linux/Windows, le terminal garde ses Ctrl.
67+ - **Palette de commandes** (`CommandPalette.svelte`) : channels, conversations et fichiers des agents, actions ; recherche floue, ↑↓ Entrée Échap, rôles ARIA combobox/listbox.
68+ - **Audit** : test de contraste WCAG AA (`lib/contrast.test.ts`) sur les couleurs du thème lues dans `style.css` ; quatre couleurs du thème clair assombries pour passer 4.5:1 ; `:focus-visible` partout.
69+
70+ Docs : référence « Raccourcis clavier » (EN/FR).
modified .tickets/issues/0036-packaging-et-distribution-macos.yaml +30 -6
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-29T07:20:34.000Z
9-updatedAt: 2026-10-29T07:20:34.000Z
9+updatedAt: 2026-10-30T07:39:01.000Z
1010 labels:
1111 - "kind::chore"
1212 - "area::infra"
@@ -29,34 +29,58 @@ body: |
2929 tasks:
3030 - id: 1
3131 title: "Build universel macOS"
32- state: open
32+ state: closed
3333 priority: high
3434 author:
3535 name: k33g
3636 email: ph.charriere@gmail.com
3737 createdAt: 2026-10-29T07:20:34.000Z
38+ updatedAt: 2026-10-30T07:39:01.000Z
39+ closedAt: 2026-10-30T07:39:01.000Z
3840 - id: 2
3941 title: "Icône et métadonnées"
40- state: open
42+ state: closed
4143 priority: medium
4244 author:
4345 name: k33g
4446 email: ph.charriere@gmail.com
4547 createdAt: 2026-10-29T07:20:34.000Z
48+ updatedAt: 2026-10-30T07:39:01.000Z
49+ closedAt: 2026-10-30T07:39:01.000Z
4650 - id: 3
4751 title: "Signature et notarisation (si possible)"
48- state: open
52+ state: closed
4953 priority: medium
5054 author:
5155 name: k33g
5256 email: ph.charriere@gmail.com
5357 createdAt: 2026-10-29T07:20:34.000Z
58+ updatedAt: 2026-10-30T07:39:01.000Z
59+ closedAt: 2026-10-30T07:39:01.000Z
5460 - id: 4
5561 title: "Vérification des prérequis au démarrage"
56- state: open
62+ state: closed
5763 priority: medium
5864 author:
5965 name: k33g
6066 email: ph.charriere@gmail.com
6167 createdAt: 2026-10-29T07:20:34.000Z
62-comments: []
68+ updatedAt: 2026-10-30T07:39:01.000Z
69+ closedAt: 2026-10-30T07:39:01.000Z
70+comments:
71+ - id: 1
72+ author:
73+ name: k33g
74+ email: ph.charriere@gmail.com
75+ createdAt: 2026-10-30T07:39:01.000Z
76+ body: |
77+ Livré sur la branche `feature/1st-release`, avec l'élargissement demandé (Linux, Windows et workflow de release).
78+
79+ - **Version 0.1.0** dans `build/config.yml`, métadonnées régénérées (Info.plist, ressources Windows, nfpm) ; icône Orbite.
80+ - **macOS** : `make package-macos` → `.dmg` et `.zip` universels (arm64 + amd64, signés ad hoc). Pas de compte Apple Developer : pas de notarisation ; procédure Gatekeeper documentée (« Ouvrir quand même », ou `xattr`).
81+ - **Linux** : `make package-linux` → `.deb`, `.rpm`, `.AppImage`, `.tar.gz` (vérifiés en arm64 dans la sandbox ; l'AppImage démarre). Licence, page d'accueil et entrée de bureau corrigées.
82+ - **Windows** : `make package-windows` → installateur NSIS (avec WebView2) et `.zip`, compilés depuis Linux. Limite : pas d'onglet Terminal sous Windows.
83+ - **Workflow** `.github/workflows/release.yml` : à la publication d'une release, vérifie tag = version, `make check`, construit Linux amd64 et Windows, `SHA256SUMS`, attache le tout à la release. Les runners de Rickub sont Linux x86-64 seulement : les paquets macOS se construisent sur le Mac et s'attachent à la main.
84+ - **Prérequis au démarrage** : le PATH du shell est lu au lancement (`internal/shellpath`), les avertissements « programme introuvable » sont calculés avec lui, et l'erreur au démarrage d'un agent dit quoi faire.
85+
86+ Docs : « Installer Sidekicks », « Publier une release », référence commandes (`package-*`, `scripts/package.sh`, workflows) (EN/FR).
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-29T07:20:34.000Z8 createdAt: 2026-10-29T07:20:34.000Z
9-updatedAt: 2026-10-29T07:20:34.000Z9+updatedAt: 2026-10-30T07:39:01.000Z
10 labels:10 labels:
11 - "kind::chore"11 - "kind::chore"
12 - "area::infra"12 - "area::infra"
@@ -29,34 +29,58 @@ body: |
29 tasks:29 tasks:
30 - id: 130 - id: 1
31 title: "Build universel macOS"31 title: "Build universel macOS"
32- state: open32+ state: closed
33 priority: high33 priority: high
34 author:34 author:
35 name: k33g35 name: k33g
36 email: ph.charriere@gmail.com36 email: ph.charriere@gmail.com
37 createdAt: 2026-10-29T07:20:34.000Z37 createdAt: 2026-10-29T07:20:34.000Z
38+ updatedAt: 2026-10-30T07:39:01.000Z
39+ closedAt: 2026-10-30T07:39:01.000Z
38 - id: 240 - id: 2
39 title: "Icône et métadonnées"41 title: "Icône et métadonnées"
40- state: open42+ state: closed
41 priority: medium43 priority: medium
42 author:44 author:
43 name: k33g45 name: k33g
44 email: ph.charriere@gmail.com46 email: ph.charriere@gmail.com
45 createdAt: 2026-10-29T07:20:34.000Z47 createdAt: 2026-10-29T07:20:34.000Z
48+ updatedAt: 2026-10-30T07:39:01.000Z
49+ closedAt: 2026-10-30T07:39:01.000Z
46 - id: 350 - id: 3
47 title: "Signature et notarisation (si possible)"51 title: "Signature et notarisation (si possible)"
48- state: open52+ state: closed
49 priority: medium53 priority: medium
50 author:54 author:
51 name: k33g55 name: k33g
52 email: ph.charriere@gmail.com56 email: ph.charriere@gmail.com
53 createdAt: 2026-10-29T07:20:34.000Z57 createdAt: 2026-10-29T07:20:34.000Z
58+ updatedAt: 2026-10-30T07:39:01.000Z
59+ closedAt: 2026-10-30T07:39:01.000Z
54 - id: 460 - id: 4
55 title: "Vérification des prérequis au démarrage"61 title: "Vérification des prérequis au démarrage"
56- state: open62+ state: closed
57 priority: medium63 priority: medium
58 author:64 author:
59 name: k33g65 name: k33g
60 email: ph.charriere@gmail.com66 email: ph.charriere@gmail.com
61 createdAt: 2026-10-29T07:20:34.000Z67 createdAt: 2026-10-29T07:20:34.000Z
62-comments: []68+ updatedAt: 2026-10-30T07:39:01.000Z
69+ closedAt: 2026-10-30T07:39:01.000Z
70+comments:
71+ - id: 1
72+ author:
73+ name: k33g
74+ email: ph.charriere@gmail.com
75+ createdAt: 2026-10-30T07:39:01.000Z
76+ body: |
77+ Livré sur la branche `feature/1st-release`, avec l'élargissement demandé (Linux, Windows et workflow de release).
78+
79+ - **Version 0.1.0** dans `build/config.yml`, métadonnées régénérées (Info.plist, ressources Windows, nfpm) ; icône Orbite.
80+ - **macOS** : `make package-macos` → `.dmg` et `.zip` universels (arm64 + amd64, signés ad hoc). Pas de compte Apple Developer : pas de notarisation ; procédure Gatekeeper documentée (« Ouvrir quand même », ou `xattr`).
81+ - **Linux** : `make package-linux` → `.deb`, `.rpm`, `.AppImage`, `.tar.gz` (vérifiés en arm64 dans la sandbox ; l'AppImage démarre). Licence, page d'accueil et entrée de bureau corrigées.
82+ - **Windows** : `make package-windows` → installateur NSIS (avec WebView2) et `.zip`, compilés depuis Linux. Limite : pas d'onglet Terminal sous Windows.
83+ - **Workflow** `.github/workflows/release.yml` : à la publication d'une release, vérifie tag = version, `make check`, construit Linux amd64 et Windows, `SHA256SUMS`, attache le tout à la release. Les runners de Rickub sont Linux x86-64 seulement : les paquets macOS se construisent sur le Mac et s'attachent à la main.
84+ - **Prérequis au démarrage** : le PATH du shell est lu au lancement (`internal/shellpath`), les avertissements « programme introuvable » sont calculés avec lui, et l'erreur au démarrage d'un agent dit quoi faire.
85+
86+ Docs : « Installer Sidekicks », « Publier une release », référence commandes (`package-*`, `scripts/package.sh`, workflows) (EN/FR).
modified .tickets/issues/0037-documentation-utilisateur-et-developpeur.yaml +26 -6
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-29T07:20:35.000Z
9-updatedAt: 2026-10-29T07:20:35.000Z
9+updatedAt: 2026-10-30T07:39:01.000Z
1010 labels:
1111 - "kind::chore"
1212 - "documentation"
@@ -33,34 +33,54 @@ body: |
3333 tasks:
3434 - id: 1
3535 title: "Tutoriel première conversation"
36- state: open
36+ state: closed
3737 priority: high
3838 author:
3939 name: k33g
4040 email: ph.charriere@gmail.com
4141 createdAt: 2026-10-29T07:20:35.000Z
42+ updatedAt: 2026-10-30T07:39:01.000Z
43+ closedAt: 2026-10-30T07:39:01.000Z
4244 - id: 2
4345 title: "Guides pratiques"
44- state: open
46+ state: closed
4547 priority: medium
4648 author:
4749 name: k33g
4850 email: ph.charriere@gmail.com
4951 createdAt: 2026-10-29T07:20:35.000Z
52+ updatedAt: 2026-10-30T07:39:01.000Z
53+ closedAt: 2026-10-30T07:39:01.000Z
5054 - id: 3
5155 title: "Référence de la configuration"
52- state: open
56+ state: closed
5357 priority: high
5458 author:
5559 name: k33g
5660 email: ph.charriere@gmail.com
5761 createdAt: 2026-10-29T07:20:35.000Z
62+ updatedAt: 2026-10-30T07:39:01.000Z
63+ closedAt: 2026-10-30T07:39:01.000Z
5864 - id: 4
5965 title: "Explications et architecture"
60- state: open
66+ state: closed
6167 priority: medium
6268 author:
6369 name: k33g
6470 email: ph.charriere@gmail.com
6571 createdAt: 2026-10-29T07:20:35.000Z
66-comments: []
72+ updatedAt: 2026-10-30T07:39:01.000Z
73+ closedAt: 2026-10-30T07:39:01.000Z
74+comments:
75+ - id: 1
76+ author:
77+ name: k33g
78+ email: ph.charriere@gmail.com
79+ createdAt: 2026-10-30T07:39:01.000Z
80+ body: |
81+ Livré sur la branche `feature/1st-release`. Contenu minimal du ticket, en anglais et en français :
82+
83+ - **Tutoriel** : « Votre première conversation avec un agent » existait ; ajout de « Votre premier agent, sans écrire de configuration » (application installée + assistant).
84+ - **Guides** : « Ajouter un agent » (nouveau), « Lancer un agent dans une sandbox sbx » et « Gérer les channels et leurs agents » (existants), plus « Installer Sidekicks », « Publier une release », « Dérouler les scénarios de bout en bout ».
85+ - **Référence** : configuration (section « Ce qu'écrit l'assistant »), « Raccourcis clavier » (nouvelle), commandes (packaging, workflows).
86+ - **Explications** : « Pourquoi ACP » et « Architecture » (existantes) ; architecture complétée (PATH du shell, erreurs en phrases claires) ; schéma des packages avec `internal/shellpath`.
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-29T07:20:35.000Z8 createdAt: 2026-10-29T07:20:35.000Z
9-updatedAt: 2026-10-29T07:20:35.000Z9+updatedAt: 2026-10-30T07:39:01.000Z
10 labels:10 labels:
11 - "kind::chore"11 - "kind::chore"
12 - "documentation"12 - "documentation"
@@ -33,34 +33,54 @@ body: |
33 tasks:33 tasks:
34 - id: 134 - id: 1
35 title: "Tutoriel première conversation"35 title: "Tutoriel première conversation"
36- state: open36+ state: closed
37 priority: high37 priority: high
38 author:38 author:
39 name: k33g39 name: k33g
40 email: ph.charriere@gmail.com40 email: ph.charriere@gmail.com
41 createdAt: 2026-10-29T07:20:35.000Z41 createdAt: 2026-10-29T07:20:35.000Z
42+ updatedAt: 2026-10-30T07:39:01.000Z
43+ closedAt: 2026-10-30T07:39:01.000Z
42 - id: 244 - id: 2
43 title: "Guides pratiques"45 title: "Guides pratiques"
44- state: open46+ state: closed
45 priority: medium47 priority: medium
46 author:48 author:
47 name: k33g49 name: k33g
48 email: ph.charriere@gmail.com50 email: ph.charriere@gmail.com
49 createdAt: 2026-10-29T07:20:35.000Z51 createdAt: 2026-10-29T07:20:35.000Z
52+ updatedAt: 2026-10-30T07:39:01.000Z
53+ closedAt: 2026-10-30T07:39:01.000Z
50 - id: 354 - id: 3
51 title: "Référence de la configuration"55 title: "Référence de la configuration"
52- state: open56+ state: closed
53 priority: high57 priority: high
54 author:58 author:
55 name: k33g59 name: k33g
56 email: ph.charriere@gmail.com60 email: ph.charriere@gmail.com
57 createdAt: 2026-10-29T07:20:35.000Z61 createdAt: 2026-10-29T07:20:35.000Z
62+ updatedAt: 2026-10-30T07:39:01.000Z
63+ closedAt: 2026-10-30T07:39:01.000Z
58 - id: 464 - id: 4
59 title: "Explications et architecture"65 title: "Explications et architecture"
60- state: open66+ state: closed
61 priority: medium67 priority: medium
62 author:68 author:
63 name: k33g69 name: k33g
64 email: ph.charriere@gmail.com70 email: ph.charriere@gmail.com
65 createdAt: 2026-10-29T07:20:35.000Z71 createdAt: 2026-10-29T07:20:35.000Z
66-comments: []72+ updatedAt: 2026-10-30T07:39:01.000Z
73+ closedAt: 2026-10-30T07:39:01.000Z
74+comments:
75+ - id: 1
76+ author:
77+ name: k33g
78+ email: ph.charriere@gmail.com
79+ createdAt: 2026-10-30T07:39:01.000Z
80+ body: |
81+ Livré sur la branche `feature/1st-release`. Contenu minimal du ticket, en anglais et en français :
82+
83+ - **Tutoriel** : « Votre première conversation avec un agent » existait ; ajout de « Votre premier agent, sans écrire de configuration » (application installée + assistant).
84+ - **Guides** : « Ajouter un agent » (nouveau), « Lancer un agent dans une sandbox sbx » et « Gérer les channels et leurs agents » (existants), plus « Installer Sidekicks », « Publier une release », « Dérouler les scénarios de bout en bout ».
85+ - **Référence** : configuration (section « Ce qu'écrit l'assistant »), « Raccourcis clavier » (nouvelle), commandes (packaging, workflows).
86+ - **Explications** : « Pourquoi ACP » et « Architecture » (existantes) ; architecture complétée (PATH du shell, erreurs en phrases claires) ; schéma des packages avec `internal/shellpath`.
modified Makefile +18 -1
@@ -6,7 +6,7 @@
66 GOLANGCI_LINT := go tool -modfile=tools/go.mod golangci-lint
77 PNPM := pnpm --dir frontend
88
9-.PHONY: help install bindings frontend dev build package install-test-config \
9+.PHONY: help install bindings frontend dev build package package-linux package-windows package-macos release-build release-tag install-test-config \
1010 check test test-go test-frontend test-scripts test-real \
1111 lint lint-go lint-frontend lint-headers bindings-check
1212
@@ -37,6 +37,21 @@ build: ## Build the application into bin/
3737 package: ## Package the application (macOS: bin/sidekicks.app)
3838 @wails3 package
3939
40+package-linux: ## Release packages for Linux (deb, rpm, AppImage, tar.gz) into dist/
41+ @scripts/package.sh linux
42+
43+package-windows: ## Release packages for Windows amd64 (installer, zip) into dist/, cross-built
44+ @scripts/package.sh windows
45+
46+package-macos: ## Release packages for macOS (universal dmg, zip) into dist/; on macOS only
47+ @scripts/package.sh macos
48+
49+release-build: ## On the Mac: every release package (macOS, Windows, Linux in Docker) and SHA256SUMS into dist/
50+ @scripts/build-release.sh
51+
52+release-tag: ## Tag v<version> on main and push it: the Release workflow creates the release
53+ @scripts/tag-release.sh
54+
4055 install-test-config: ## Write the test agents configuration at the default location (absolute paths)
4156 @scripts/install-test-config.sh
4257
@@ -56,6 +71,8 @@ test-frontend: ## Frontend unit tests (vitest)
5671 test-scripts: ## Tests of the shell tooling
5772 @scripts/agents_test.sh
5873 @scripts/check-license-headers_test.sh
74+ @scripts/package_test.sh
75+ @scripts/tag-release_test.sh
5976
6077 test-real: ## Tests against the real mm agents (needs mm and llmman; 2 agents at most)
6178 @go test -tags realagents -count=1 ./...
@@ -6,7 +6,7 @@
6 GOLANGCI_LINT := go tool -modfile=tools/go.mod golangci-lint6 GOLANGCI_LINT := go tool -modfile=tools/go.mod golangci-lint
7 PNPM := pnpm --dir frontend7 PNPM := pnpm --dir frontend
8 8
9-.PHONY: help install bindings frontend dev build package install-test-config \9+.PHONY: help install bindings frontend dev build package package-linux package-windows package-macos release-build release-tag install-test-config \
10 check test test-go test-frontend test-scripts test-real \10 check test test-go test-frontend test-scripts test-real \
11 lint lint-go lint-frontend lint-headers bindings-check11 lint lint-go lint-frontend lint-headers bindings-check
12 12
@@ -37,6 +37,21 @@ build: ## Build the application into bin/
37 package: ## Package the application (macOS: bin/sidekicks.app)37 package: ## Package the application (macOS: bin/sidekicks.app)
38 @wails3 package38 @wails3 package
39 39
40+package-linux: ## Release packages for Linux (deb, rpm, AppImage, tar.gz) into dist/
41+ @scripts/package.sh linux
42+
43+package-windows: ## Release packages for Windows amd64 (installer, zip) into dist/, cross-built
44+ @scripts/package.sh windows
45+
46+package-macos: ## Release packages for macOS (universal dmg, zip) into dist/; on macOS only
47+ @scripts/package.sh macos
48+
49+release-build: ## On the Mac: every release package (macOS, Windows, Linux in Docker) and SHA256SUMS into dist/
50+ @scripts/build-release.sh
51+
52+release-tag: ## Tag v<version> on main and push it: the Release workflow creates the release
53+ @scripts/tag-release.sh
54+
40 install-test-config: ## Write the test agents configuration at the default location (absolute paths)55 install-test-config: ## Write the test agents configuration at the default location (absolute paths)
41 @scripts/install-test-config.sh56 @scripts/install-test-config.sh
42 57
@@ -56,6 +71,8 @@ test-frontend: ## Frontend unit tests (vitest)
56 test-scripts: ## Tests of the shell tooling71 test-scripts: ## Tests of the shell tooling
57 @scripts/agents_test.sh72 @scripts/agents_test.sh
58 @scripts/check-license-headers_test.sh73 @scripts/check-license-headers_test.sh
74+ @scripts/package_test.sh
75+ @scripts/tag-release_test.sh
59 76
60 test-real: ## Tests against the real mm agents (needs mm and llmman; 2 agents at most)77 test-real: ## Tests against the real mm agents (needs mm and llmman; 2 agents at most)
61 @go test -tags realagents -count=1 ./...78 @go test -tags realagents -count=1 ./...
modified README.md +14 -3
@@ -1,10 +1,19 @@
1+<p align="center">
2+ <picture>
3+ <source media="(prefers-color-scheme: dark)" srcset="docs/design/logo/sidekicks-mark-dark.svg">
4+ <img src="docs/design/logo/sidekicks-mark.svg" alt="Sidekicks logo: a centre dot with two small dots in orbit" width="120">
5+ </picture>
6+</p>
7+
18 # Sidekicks
29
310 A desktop application to talk with AI agents in channels, for people who are not developers. Agents are reached through the [Agent Client Protocol](https://agentclientprotocol.com) (ACP), so any ACP agent can join. Built with Go and [Wails v3](https://wails.io), interface in Svelte 5.
411
5-> **Status — milestone M4 (Agents' workspace)**: channels kept from one launch to the next, several agents per channel with `@` mentions, agent commands with `/`, direct conversations, and each agent's folder with an editor and a terminal (local or Docker container); answers stream as Markdown with coloured code, tool calls ask permission in plain words, and the configuration is edited from the window. Backlog and roadmap: `.tickets/` (IssueSpec).
12+> **Status — version 0.1, the first release**: packages for macOS, Linux and Windows; an assistant adds agents (on this computer, in Docker or in an sbx sandbox) without writing YAML; channels kept from one launch to the next, several agents per channel with `@` mentions, agent commands with `/`, direct conversations, each agent's folder with an editor and a terminal; answers stream as Markdown with coloured code, tool calls ask permission in plain words, errors say what to do, and a command palette (⌘K) and shortcuts drive it from the keyboard. Backlog and roadmap: `.tickets/` (IssueSpec).
13+
14+**Install**: download the package for your system from the [releases](https://rickub.com/bots-garden/sidekicks/releases), then follow [How to install Sidekicks](docs/en/how-to/install-sidekicks.md).
615
7-Documentation: [English](docs/en/README.md) · [Français](docs/fr/README.md) · [decisions (ADR)](docs/adr/) · [wireframes](docs/design/README.md)
16+Documentation: [English](docs/en/README.md) · [Français](docs/fr/README.md) · [decisions (ADR)](docs/adr/) · [wireframes](docs/design/README.md) · [releasing](RELEASING.md)
817
918 ## Develop
1019
@@ -15,6 +24,7 @@ make install # frontend dependencies (macOS and Linux binaries side by sid
1524 make dev # run the application with hot reload
1625 make check # lint + every test, as the CI does
1726 make package # macOS: bin/sidekicks.app
27+make package-linux # release packages into dist/ (also package-windows, package-macos)
1828 make help # every target
1929 ```
2030
@@ -40,9 +50,10 @@ Details: [How to develop the application](docs/en/how-to/develop-the-application
4050 | `internal/acp` | ACP v1 client over stdio (wraps `coder/acp-go-sdk`) |
4151 | `internal/config` | Configuration file: load, validate, locate, create, save, watch |
4252 | `internal/events` | Events sent from Go to the interface |
53+| `internal/shellpath` | The `PATH` of the user's shell, for an application opened from the Finder |
4354 | `schema` | JSON Schema of the configuration file |
4455 | `frontend` | Svelte 5 interface: panels, shell, state |
45-| `scripts` | Test-agent tooling (`agents.sh`), licence header check |
56+| `scripts` | Test-agent tooling (`agents.sh`), licence header check, release packages (`package.sh`) |
4657 | `agents`, `examples` | The four test agents and a configuration using them |
4758 | `spikes` | Throwaway prototypes of milestone Initialize |
4859
@@ -1,10 +1,19 @@
1+<p align="center">
2+ <picture>
3+ <source media="(prefers-color-scheme: dark)" srcset="docs/design/logo/sidekicks-mark-dark.svg">
4+ <img src="docs/design/logo/sidekicks-mark.svg" alt="Sidekicks logo: a centre dot with two small dots in orbit" width="120">
5+ </picture>
6+</p>
7+
1 # Sidekicks8 # Sidekicks
2 9
3 A desktop application to talk with AI agents in channels, for people who are not developers. Agents are reached through the [Agent Client Protocol](https://agentclientprotocol.com) (ACP), so any ACP agent can join. Built with Go and [Wails v3](https://wails.io), interface in Svelte 5.10 A desktop application to talk with AI agents in channels, for people who are not developers. Agents are reached through the [Agent Client Protocol](https://agentclientprotocol.com) (ACP), so any ACP agent can join. Built with Go and [Wails v3](https://wails.io), interface in Svelte 5.
4 11
5-> **Status — milestone M4 (Agents' workspace)**: channels kept from one launch to the next, several agents per channel with `@` mentions, agent commands with `/`, direct conversations, and each agent's folder with an editor and a terminal (local or Docker container); answers stream as Markdown with coloured code, tool calls ask permission in plain words, and the configuration is edited from the window. Backlog and roadmap: `.tickets/` (IssueSpec).12+> **Status — version 0.1, the first release**: packages for macOS, Linux and Windows; an assistant adds agents (on this computer, in Docker or in an sbx sandbox) without writing YAML; channels kept from one launch to the next, several agents per channel with `@` mentions, agent commands with `/`, direct conversations, each agent's folder with an editor and a terminal; answers stream as Markdown with coloured code, tool calls ask permission in plain words, errors say what to do, and a command palette (⌘K) and shortcuts drive it from the keyboard. Backlog and roadmap: `.tickets/` (IssueSpec).
13+
14+**Install**: download the package for your system from the [releases](https://rickub.com/bots-garden/sidekicks/releases), then follow [How to install Sidekicks](docs/en/how-to/install-sidekicks.md).
6 15
7-Documentation: [English](docs/en/README.md) · [Français](docs/fr/README.md) · [decisions (ADR)](docs/adr/) · [wireframes](docs/design/README.md)16+Documentation: [English](docs/en/README.md) · [Français](docs/fr/README.md) · [decisions (ADR)](docs/adr/) · [wireframes](docs/design/README.md) · [releasing](RELEASING.md)
8 17
9 ## Develop18 ## Develop
10 19
@@ -15,6 +24,7 @@ make install # frontend dependencies (macOS and Linux binaries side by sid
15 make dev # run the application with hot reload24 make dev # run the application with hot reload
16 make check # lint + every test, as the CI does25 make check # lint + every test, as the CI does
17 make package # macOS: bin/sidekicks.app26 make package # macOS: bin/sidekicks.app
27+make package-linux # release packages into dist/ (also package-windows, package-macos)
18 make help # every target28 make help # every target
19 ```29 ```
20 30
@@ -40,9 +50,10 @@ Details: [How to develop the application](docs/en/how-to/develop-the-application
40 | `internal/acp` | ACP v1 client over stdio (wraps `coder/acp-go-sdk`) |50 | `internal/acp` | ACP v1 client over stdio (wraps `coder/acp-go-sdk`) |
41 | `internal/config` | Configuration file: load, validate, locate, create, save, watch |51 | `internal/config` | Configuration file: load, validate, locate, create, save, watch |
42 | `internal/events` | Events sent from Go to the interface |52 | `internal/events` | Events sent from Go to the interface |
53+| `internal/shellpath` | The `PATH` of the user's shell, for an application opened from the Finder |
43 | `schema` | JSON Schema of the configuration file |54 | `schema` | JSON Schema of the configuration file |
44 | `frontend` | Svelte 5 interface: panels, shell, state |55 | `frontend` | Svelte 5 interface: panels, shell, state |
45-| `scripts` | Test-agent tooling (`agents.sh`), licence header check |56+| `scripts` | Test-agent tooling (`agents.sh`), licence header check, release packages (`package.sh`) |
46 | `agents`, `examples` | The four test agents and a configuration using them |57 | `agents`, `examples` | The four test agents and a configuration using them |
47 | `spikes` | Throwaway prototypes of milestone Initialize |58 | `spikes` | Throwaway prototypes of milestone Initialize |
48 59
added RELEASING.fr.md +59 -0
new file mode 100644
@@ -0,0 +1,59 @@
1+# Publier une version de Sidekicks
2+
3+[English](RELEASING.md)
4+
5+Trois étapes, sur le Mac : tout construire, tagger, attacher les fichiers.
6+
7+| Étape | Commande | Résultat |
8+| --- | --- | --- |
9+| 1. Construire | `make release-build` | `dist/` : les paquets macOS, Windows et Linux, et `SHA256SUMS` |
10+| 2. Tagger | `make release-tag` | le tag `v<version>` est poussé ; le workflow **Release** crée la release sur Rickub, avec ses notes |
11+| 3. Attacher | à la main, sur la page de la release | tous les fichiers de `dist/` attachés à la release |
12+
13+La version est `info.version` dans `build/config.yml` ; le tag est `v` + cette version.
14+
15+## Avant : fixer la version
16+
17+1. Dans `build/config.yml`, réglez `info.version` (par exemple `"0.2.0"`).
18+2. `wails3 task common:update:build-assets`, puis remettez `LSMinimumSystemVersion` à `13.0.0` dans `build/darwin/Info.plist` et `build/darwin/Info.dev.plist` (la commande le remet à `12.0.0`).
19+3. `make check`, commitez, fusionnez dans `main`, puis `git checkout main && git pull`.
20+
21+## 1. Construire
22+
23+```bash
24+make release-build
25+```
26+
27+Il construit, dans `dist/` :
28+
29+| Fichiers | Comment |
30+| --- | --- |
31+| `Sidekicks-<v>-macos-universal.dmg`, `.zip` | en natif (outils en ligne de commande de Xcode) |
32+| `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` | compilés depuis le Mac (demande `brew install makensis zip`) |
33+| `sidekicks_<v>_linux_amd64.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-x86_64.AppImage` | dans un conteneur Docker (Docker doit tourner ; amd64 est émulé sur Apple Silicon, c'est donc un peu long) |
34+| `SHA256SUMS` | sur tous les fichiers ci-dessus |
35+
36+`LINUX_ARCHES="amd64 arm64" make release-build` ajoute les paquets Linux arm64 ; `SKIP="linux"` (ou `macos`, `windows`) laisse un système de côté.
37+
38+## 2. Tagger
39+
40+```bash
41+make release-tag
42+```
43+
44+Il refuse si la copie de travail a des modifications, si vous n'êtes pas sur `main`, si `main` diffère de `origin/main`, ou si le tag existe ; il avertit s'il manque un paquet dans `dist/`. Puis il crée le tag annoté `v<version>` et le pousse. Le push lance `.github/workflows/release.yml`, qui vérifie que le tag correspond à `build/config.yml` et crée la release avec des notes qui listent les fichiers attendus.
45+
46+## 3. Attacher
47+
48+Ouvrez la release sur Rickub (**Releases → v<version>**) et attachez tous les fichiers de `dist/`, `SHA256SUMS` compris.
49+
50+## En cas de problème
51+
52+| Symptôme | Que faire |
53+| --- | --- |
54+| `makensis is missing` / `Docker is not running` | `brew install makensis zip` / lancez Docker Desktop — ou laissez ce système de côté avec `SKIP`. |
55+| `the tag v… already exists` | Cette version a déjà été publiée : augmentez `info.version`. |
56+| Le workflow a échoué : « The tag is … but build/config.yml says … » | Supprimez le tag (`git push origin :refs/tags/v…` et `git tag -d v…`), corrigez `info.version`, recommencez. |
57+| Le workflow a tourné mais aucune release n'est apparue | Le journal du job dit pourquoi (un `403` veut dire que le jeton n'a pas pu écrire : `permissions: contents: write` doit rester dans `release.yml`). Créez la release à la main sur Rickub depuis le tag ; les notes sont dans le journal du job. |
58+
59+Détails : [Publier une release](docs/fr/how-to/publier-une-release.md) · [référence des commandes](docs/fr/reference/commandes.md).
new file mode 100644
@@ -0,0 +1,59 @@
1+# Publier une version de Sidekicks
2+
3+[English](RELEASING.md)
4+
5+Trois étapes, sur le Mac : tout construire, tagger, attacher les fichiers.
6+
7+| Étape | Commande | Résultat |
8+| --- | --- | --- |
9+| 1. Construire | `make release-build` | `dist/` : les paquets macOS, Windows et Linux, et `SHA256SUMS` |
10+| 2. Tagger | `make release-tag` | le tag `v<version>` est poussé ; le workflow **Release** crée la release sur Rickub, avec ses notes |
11+| 3. Attacher | à la main, sur la page de la release | tous les fichiers de `dist/` attachés à la release |
12+
13+La version est `info.version` dans `build/config.yml` ; le tag est `v` + cette version.
14+
15+## Avant : fixer la version
16+
17+1. Dans `build/config.yml`, réglez `info.version` (par exemple `"0.2.0"`).
18+2. `wails3 task common:update:build-assets`, puis remettez `LSMinimumSystemVersion` à `13.0.0` dans `build/darwin/Info.plist` et `build/darwin/Info.dev.plist` (la commande le remet à `12.0.0`).
19+3. `make check`, commitez, fusionnez dans `main`, puis `git checkout main && git pull`.
20+
21+## 1. Construire
22+
23+```bash
24+make release-build
25+```
26+
27+Il construit, dans `dist/` :
28+
29+| Fichiers | Comment |
30+| --- | --- |
31+| `Sidekicks-<v>-macos-universal.dmg`, `.zip` | en natif (outils en ligne de commande de Xcode) |
32+| `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` | compilés depuis le Mac (demande `brew install makensis zip`) |
33+| `sidekicks_<v>_linux_amd64.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-x86_64.AppImage` | dans un conteneur Docker (Docker doit tourner ; amd64 est émulé sur Apple Silicon, c'est donc un peu long) |
34+| `SHA256SUMS` | sur tous les fichiers ci-dessus |
35+
36+`LINUX_ARCHES="amd64 arm64" make release-build` ajoute les paquets Linux arm64 ; `SKIP="linux"` (ou `macos`, `windows`) laisse un système de côté.
37+
38+## 2. Tagger
39+
40+```bash
41+make release-tag
42+```
43+
44+Il refuse si la copie de travail a des modifications, si vous n'êtes pas sur `main`, si `main` diffère de `origin/main`, ou si le tag existe ; il avertit s'il manque un paquet dans `dist/`. Puis il crée le tag annoté `v<version>` et le pousse. Le push lance `.github/workflows/release.yml`, qui vérifie que le tag correspond à `build/config.yml` et crée la release avec des notes qui listent les fichiers attendus.
45+
46+## 3. Attacher
47+
48+Ouvrez la release sur Rickub (**Releases → v<version>**) et attachez tous les fichiers de `dist/`, `SHA256SUMS` compris.
49+
50+## En cas de problème
51+
52+| Symptôme | Que faire |
53+| --- | --- |
54+| `makensis is missing` / `Docker is not running` | `brew install makensis zip` / lancez Docker Desktop — ou laissez ce système de côté avec `SKIP`. |
55+| `the tag v… already exists` | Cette version a déjà été publiée : augmentez `info.version`. |
56+| Le workflow a échoué : « The tag is … but build/config.yml says … » | Supprimez le tag (`git push origin :refs/tags/v…` et `git tag -d v…`), corrigez `info.version`, recommencez. |
57+| Le workflow a tourné mais aucune release n'est apparue | Le journal du job dit pourquoi (un `403` veut dire que le jeton n'a pas pu écrire : `permissions: contents: write` doit rester dans `release.yml`). Créez la release à la main sur Rickub depuis le tag ; les notes sont dans le journal du job. |
58+
59+Détails : [Publier une release](docs/fr/how-to/publier-une-release.md) · [référence des commandes](docs/fr/reference/commandes.md).
added RELEASING.md +59 -0
new file mode 100644
@@ -0,0 +1,59 @@
1+# Releasing Sidekicks
2+
3+[Français](RELEASING.fr.md)
4+
5+Three steps, on the Mac: build everything, tag, attach the files.
6+
7+| Step | Command | Result |
8+| --- | --- | --- |
9+| 1. Build | `make release-build` | `dist/`: the macOS, Windows and Linux packages, and `SHA256SUMS` |
10+| 2. Tag | `make release-tag` | the tag `v<version>` is pushed; the **Release** workflow creates the release on Rickub, with its notes |
11+| 3. Attach | by hand, on the release page | every file of `dist/` attached to the release |
12+
13+The version is `info.version` in `build/config.yml`; the tag is `v` + that version.
14+
15+## Before: set the version
16+
17+1. In `build/config.yml`, set `info.version` (e.g. `"0.2.0"`).
18+2. `wails3 task common:update:build-assets`, then put `LSMinimumSystemVersion` back to `13.0.0` in `build/darwin/Info.plist` and `build/darwin/Info.dev.plist` (the command resets it to `12.0.0`).
19+3. `make check`, commit, merge into `main`, and `git checkout main && git pull`.
20+
21+## 1. Build
22+
23+```bash
24+make release-build
25+```
26+
27+It builds, into `dist/`:
28+
29+| Files | How |
30+| --- | --- |
31+| `Sidekicks-<v>-macos-universal.dmg`, `.zip` | natively (Xcode command line tools) |
32+| `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` | cross-built (needs `brew install makensis zip`) |
33+| `sidekicks_<v>_linux_amd64.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-x86_64.AppImage` | in a Docker container (needs Docker running; amd64 is emulated on Apple Silicon, so it takes a while) |
34+| `SHA256SUMS` | over all the files above |
35+
36+`LINUX_ARCHES="amd64 arm64" make release-build` adds the Linux arm64 packages; `SKIP="linux"` (or `macos`, `windows`) leaves a system out.
37+
38+## 2. Tag
39+
40+```bash
41+make release-tag
42+```
43+
44+It refuses when the working tree has changes, when you are not on `main`, when `main` differs from `origin/main`, or when the tag exists; it warns when `dist/` misses a package. Then it creates the annotated tag `v<version>` and pushes it. The push starts `.github/workflows/release.yml`, which checks that the tag matches `build/config.yml` and creates the release with notes listing the files to expect.
45+
46+## 3. Attach
47+
48+Open the release on Rickub (**Releases → v<version>**), and attach every file of `dist/`, `SHA256SUMS` included.
49+
50+## When something goes wrong
51+
52+| Symptom | What to do |
53+| --- | --- |
54+| `makensis is missing` / `Docker is not running` | `brew install makensis zip` / start Docker Desktop — or leave that system out with `SKIP`. |
55+| `the tag v… already exists` | That version was released already: raise `info.version`. |
56+| The workflow failed: "The tag is … but build/config.yml says …" | Delete the tag (`git push origin :refs/tags/v…` and `git tag -d v…`), fix `info.version`, start again. |
57+| The workflow ran but no release appeared | The job's log tells why (a `403` means the token could not write: `permissions: contents: write` must stay in `release.yml`). Create the release by hand on Rickub from the tag; the notes are in the job's log. |
58+
59+Details: [How to publish a release](docs/en/how-to/publish-a-release.md) · [commands reference](docs/en/reference/commands.md).
new file mode 100644
@@ -0,0 +1,59 @@
1+# Releasing Sidekicks
2+
3+[Français](RELEASING.fr.md)
4+
5+Three steps, on the Mac: build everything, tag, attach the files.
6+
7+| Step | Command | Result |
8+| --- | --- | --- |
9+| 1. Build | `make release-build` | `dist/`: the macOS, Windows and Linux packages, and `SHA256SUMS` |
10+| 2. Tag | `make release-tag` | the tag `v<version>` is pushed; the **Release** workflow creates the release on Rickub, with its notes |
11+| 3. Attach | by hand, on the release page | every file of `dist/` attached to the release |
12+
13+The version is `info.version` in `build/config.yml`; the tag is `v` + that version.
14+
15+## Before: set the version
16+
17+1. In `build/config.yml`, set `info.version` (e.g. `"0.2.0"`).
18+2. `wails3 task common:update:build-assets`, then put `LSMinimumSystemVersion` back to `13.0.0` in `build/darwin/Info.plist` and `build/darwin/Info.dev.plist` (the command resets it to `12.0.0`).
19+3. `make check`, commit, merge into `main`, and `git checkout main && git pull`.
20+
21+## 1. Build
22+
23+```bash
24+make release-build
25+```
26+
27+It builds, into `dist/`:
28+
29+| Files | How |
30+| --- | --- |
31+| `Sidekicks-<v>-macos-universal.dmg`, `.zip` | natively (Xcode command line tools) |
32+| `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` | cross-built (needs `brew install makensis zip`) |
33+| `sidekicks_<v>_linux_amd64.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-x86_64.AppImage` | in a Docker container (needs Docker running; amd64 is emulated on Apple Silicon, so it takes a while) |
34+| `SHA256SUMS` | over all the files above |
35+
36+`LINUX_ARCHES="amd64 arm64" make release-build` adds the Linux arm64 packages; `SKIP="linux"` (or `macos`, `windows`) leaves a system out.
37+
38+## 2. Tag
39+
40+```bash
41+make release-tag
42+```
43+
44+It refuses when the working tree has changes, when you are not on `main`, when `main` differs from `origin/main`, or when the tag exists; it warns when `dist/` misses a package. Then it creates the annotated tag `v<version>` and pushes it. The push starts `.github/workflows/release.yml`, which checks that the tag matches `build/config.yml` and creates the release with notes listing the files to expect.
45+
46+## 3. Attach
47+
48+Open the release on Rickub (**Releases → v<version>**), and attach every file of `dist/`, `SHA256SUMS` included.
49+
50+## When something goes wrong
51+
52+| Symptom | What to do |
53+| --- | --- |
54+| `makensis is missing` / `Docker is not running` | `brew install makensis zip` / start Docker Desktop — or leave that system out with `SKIP`. |
55+| `the tag v… already exists` | That version was released already: raise `info.version`. |
56+| The workflow failed: "The tag is … but build/config.yml says …" | Delete the tag (`git push origin :refs/tags/v…` and `git tag -d v…`), fix `info.version`, start again. |
57+| The workflow ran but no release appeared | The job's log tells why (a `403` means the token could not write: `permissions: contents: write` must stay in `release.yml`). Create the release by hand on Rickub from the tag; the notes are in the job's log. |
58+
59+Details: [How to publish a release](docs/en/how-to/publish-a-release.md) · [commands reference](docs/en/reference/commands.md).
added agents/butterfly/agent.yaml +85 -0
new file mode 100644
@@ -0,0 +1,85 @@
1+# butterfly — answers questions, no tools.
2+#
3+# Written by `mm init`. Every value says why it has that value; change
4+# them as you watch the agent work (guides/agent-settings-for-small-models.md).
5+#
6+# model family: qwen
7+# usage: assistant
8+# machine: small — a laptop, models up to about 8B
9+# model facts: read from llmman: native window 40960 tokens; reasoning switch enable_thinking (default on)
10+#
11+# Where the numbers come from (the tag before their comment):
12+# [card] from the model card or the GGUF, not measured — re-read the new card
13+# [guide] the settings guide's row for this machine
14+# [rule] a rule of thumb, not specific to this model
15+#
16+# mm -config <this file> or mm -tui <this file>
17+
18+provider: llmman
19+# The model. Chosen for it (family qwen): sampling (temperature, top_p, top_k,
20+# max_tokens) and sampling.chat_template_kwargs (enable_thinking). Change the
21+# model and redo them together — running mm init again is the quick way.
22+model: "hf.co/Menlo/Lucy-gguf:Q4_K_M"
23+# No baseUrl: llmman's defaults apply. For the record:
24+# baseUrl: "http://127.0.0.1:17434/v1"
25+# fallback: "http://host.docker.internal:17434/v1" # tried when baseUrl does not answer
26+
27+# 0 = mm asks the server for the window it serves (the model's native window
28+# is 40960 tokens; the server may serve less)
29+contextWindow: 0
30+# usage assistant's default: tools run without asking
31+yolo: true
32+# usage assistant
33+bashTool: false
34+editTools: false
35+planTool: false
36+askUserTool: true
37+# no skills: add a skills/ folder and set skillsDir to use some (see
38+# docs/en/how-to/add-a-skill.md)
39+skillsDir: no-skills
40+# usage assistant
41+maxTurns: 5
42+# [guide] settings guide, row "Small model (≤ 8B), short tasks"
43+maxOutput: 8000
44+firstTokenTimeout: 3m
45+watchdogTimeout: 20s
46+
47+sampling:
48+ # [card] family qwen: Qwen model cards (thinking 0.6 / 0.95 / 20,
49+ # non-thinking 0.7 / 0.8 / 20); precise 0.0 measured with Qwen3.5 4B,
50+ # thinking off (demos/docs-search, memory-search, plans)
51+ temperature: 0.7
52+ top_p: 0.8
53+ top_k: 20
54+ # [rule] room for an answer or a whole file in one tool call
55+ max_tokens: 4096
56+ # one tool call per reply: small models run a check before the change it
57+ # checks has landed otherwise
58+ parallel_tool_calls: false
59+ chat_template_kwargs:
60+ # reasoning off: short, direct answers; the template's default is on (read
61+ # from the chat template)
62+ enable_thinking: false
63+
64+context:
65+ # compress the history before the window is full (settings guide, every row)
66+ enabled: true
67+ # mm's default: 75 % of the window the server serves, a quarter left for the
68+ # next question and its tool outputs
69+ threshold: 75
70+
71+agentsMd:
72+ # An AGENTS.md in the project directory is added to the system prompt.
73+ enabled: true
74+
75+acp:
76+ name: butterfly
77+ title: "Butterfly (assistant)"
78+
79+system: |
80+ Your name is Blue Butterfly. You are a helpful assistant. Answer clearly and briefly, in the user's
81+ language. Say when you do not know; never invent facts, names or
82+ figures.
83+
84+# Next: skills (docs/en/how-to/add-a-skill.md), MCP servers (docs/en/tutorials/mcp-getting-started.md),
85+# hooks (docs/en/how-to/guard-the-agent-with-hooks.md), sessions (docs/en/how-to/keep-sessions.md).
new file mode 100644
@@ -0,0 +1,85 @@
1+# butterfly — answers questions, no tools.
2+#
3+# Written by `mm init`. Every value says why it has that value; change
4+# them as you watch the agent work (guides/agent-settings-for-small-models.md).
5+#
6+# model family: qwen
7+# usage: assistant
8+# machine: small — a laptop, models up to about 8B
9+# model facts: read from llmman: native window 40960 tokens; reasoning switch enable_thinking (default on)
10+#
11+# Where the numbers come from (the tag before their comment):
12+# [card] from the model card or the GGUF, not measured — re-read the new card
13+# [guide] the settings guide's row for this machine
14+# [rule] a rule of thumb, not specific to this model
15+#
16+# mm -config <this file> or mm -tui <this file>
17+
18+provider: llmman
19+# The model. Chosen for it (family qwen): sampling (temperature, top_p, top_k,
20+# max_tokens) and sampling.chat_template_kwargs (enable_thinking). Change the
21+# model and redo them together — running mm init again is the quick way.
22+model: "hf.co/Menlo/Lucy-gguf:Q4_K_M"
23+# No baseUrl: llmman's defaults apply. For the record:
24+# baseUrl: "http://127.0.0.1:17434/v1"
25+# fallback: "http://host.docker.internal:17434/v1" # tried when baseUrl does not answer
26+
27+# 0 = mm asks the server for the window it serves (the model's native window
28+# is 40960 tokens; the server may serve less)
29+contextWindow: 0
30+# usage assistant's default: tools run without asking
31+yolo: true
32+# usage assistant
33+bashTool: false
34+editTools: false
35+planTool: false
36+askUserTool: true
37+# no skills: add a skills/ folder and set skillsDir to use some (see
38+# docs/en/how-to/add-a-skill.md)
39+skillsDir: no-skills
40+# usage assistant
41+maxTurns: 5
42+# [guide] settings guide, row "Small model (≤ 8B), short tasks"
43+maxOutput: 8000
44+firstTokenTimeout: 3m
45+watchdogTimeout: 20s
46+
47+sampling:
48+ # [card] family qwen: Qwen model cards (thinking 0.6 / 0.95 / 20,
49+ # non-thinking 0.7 / 0.8 / 20); precise 0.0 measured with Qwen3.5 4B,
50+ # thinking off (demos/docs-search, memory-search, plans)
51+ temperature: 0.7
52+ top_p: 0.8
53+ top_k: 20
54+ # [rule] room for an answer or a whole file in one tool call
55+ max_tokens: 4096
56+ # one tool call per reply: small models run a check before the change it
57+ # checks has landed otherwise
58+ parallel_tool_calls: false
59+ chat_template_kwargs:
60+ # reasoning off: short, direct answers; the template's default is on (read
61+ # from the chat template)
62+ enable_thinking: false
63+
64+context:
65+ # compress the history before the window is full (settings guide, every row)
66+ enabled: true
67+ # mm's default: 75 % of the window the server serves, a quarter left for the
68+ # next question and its tool outputs
69+ threshold: 75
70+
71+agentsMd:
72+ # An AGENTS.md in the project directory is added to the system prompt.
73+ enabled: true
74+
75+acp:
76+ name: butterfly
77+ title: "Butterfly (assistant)"
78+
79+system: |
80+ Your name is Blue Butterfly. You are a helpful assistant. Answer clearly and briefly, in the user's
81+ language. Say when you do not know; never invent facts, names or
82+ figures.
83+
84+# Next: skills (docs/en/how-to/add-a-skill.md), MCP servers (docs/en/tutorials/mcp-getting-started.md),
85+# hooks (docs/en/how-to/guard-the-agent-with-hooks.md), sessions (docs/en/how-to/keep-sessions.md).
added build/appicon.icon/Assets/sidekicks-mark.svg +12 -0
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 100 100">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
10+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 100 100">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
10+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
deleted build/appicon.icon/Assets/wails_icon_vector.svg +0 -9
deleted file mode 100644
@@ -1,9 +0,0 @@
1-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3-<svg width="100%" height="100%" viewBox="0 0 583 533" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
4- <g transform="matrix(1,0,0,1,-246,-251)">
5- <g id="Ebene1">
6- <path d="M246,251L265,784L401,784L506,450L507,450L505,784L641,784L829,251L682,251L596,567L595,567L596,251L478,251L378,568L391,251L246,251Z"/>
7- </g>
8- </g>
9-</svg>
deleted file mode 100644
@@ -1,9 +0,0 @@
1-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3-<svg width="100%" height="100%" viewBox="0 0 583 533" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
4- <g transform="matrix(1,0,0,1,-246,-251)">
5- <g id="Ebene1">
6- <path d="M246,251L265,784L401,784L506,450L507,450L505,784L641,784L829,251L682,251L596,567L595,567L596,251L478,251L378,568L391,251L246,251Z"/>
7- </g>
8- </g>
9-</svg>
modified build/appicon.icon/icon.json +19 -33
@@ -1,51 +1,37 @@
11 {
2- "fill" : {
3- "automatic-gradient" : "extended-gray:1.00000,1.00000"
2+ "fill": {
3+ "automatic-gradient": "srgb:0.11765,0.13333,0.25098,1.00000"
44 },
5- "groups" : [
5+ "groups": [
66 {
7- "layers" : [
7+ "layers": [
88 {
9- "fill-specializations" : [
10- {
11- "appearance" : "dark",
12- "value" : {
13- "solid" : "srgb:0.92143,0.92145,0.92144,1.00000"
14- }
15- },
16- {
17- "appearance" : "tinted",
18- "value" : {
19- "solid" : "srgb:0.83742,0.83744,0.83743,1.00000"
20- }
21- }
22- ],
23- "image-name" : "wails_icon_vector.svg",
24- "name" : "wails_icon_vector",
25- "position" : {
26- "scale" : 0.85,
27- "translation-in-points" : [
9+ "image-name": "sidekicks-mark.svg",
10+ "name": "sidekicks-mark",
11+ "position": {
12+ "scale": 0.8,
13+ "translation-in-points": [
2814 0.0,
2915 0.0
3016 ]
3117 }
3218 }
3319 ],
34- "shadow" : {
35- "kind" : "neutral",
36- "opacity" : 0.5
20+ "shadow": {
21+ "kind": "neutral",
22+ "opacity": 0.5
3723 },
38- "specular" : true,
39- "translucency" : {
40- "enabled" : true,
41- "value" : 0.5
24+ "specular": true,
25+ "translucency": {
26+ "enabled": true,
27+ "value": 0.5
4228 }
4329 }
4430 ],
45- "supported-platforms" : {
46- "circles" : [
31+ "supported-platforms": {
32+ "circles": [
4733 "watchOS"
4834 ],
49- "squares" : "shared"
35+ "squares": "shared"
5036 }
5137 }
@@ -1,51 +1,37 @@
1 {1 {
2- "fill" : {2+ "fill": {
3- "automatic-gradient" : "extended-gray:1.00000,1.00000"3+ "automatic-gradient": "srgb:0.11765,0.13333,0.25098,1.00000"
4 },4 },
5- "groups" : [5+ "groups": [
6 {6 {
7- "layers" : [7+ "layers": [
8 {8 {
9- "fill-specializations" : [9+ "image-name": "sidekicks-mark.svg",
10- {10+ "name": "sidekicks-mark",
11- "appearance" : "dark",11+ "position": {
12- "value" : {12+ "scale": 0.8,
13- "solid" : "srgb:0.92143,0.92145,0.92144,1.00000"13+ "translation-in-points": [
14- }
15- },
16- {
17- "appearance" : "tinted",
18- "value" : {
19- "solid" : "srgb:0.83742,0.83744,0.83743,1.00000"
20- }
21- }
22- ],
23- "image-name" : "wails_icon_vector.svg",
24- "name" : "wails_icon_vector",
25- "position" : {
26- "scale" : 0.85,
27- "translation-in-points" : [
28 0.0,14 0.0,
29 0.015 0.0
30 ]16 ]
31 }17 }
32 }18 }
33 ],19 ],
34- "shadow" : {20+ "shadow": {
35- "kind" : "neutral",21+ "kind": "neutral",
36- "opacity" : 0.522+ "opacity": 0.5
37 },23 },
38- "specular" : true,24+ "specular": true,
39- "translucency" : {25+ "translucency": {
40- "enabled" : true,26+ "enabled": true,
41- "value" : 0.527+ "value": 0.5
42 }28 }
43 }29 }
44 ],30 ],
45- "supported-platforms" : {31+ "supported-platforms": {
46- "circles" : [32+ "circles": [
47 "watchOS"33 "watchOS"
48 ],34 ],
49- "squares" : "shared"35+ "squares": "shared"
50 }36 }
51 }37 }
modified build/appicon.png +0 -0
Binary files a/build/appicon.png and b/build/appicon.png differ
Binary files a/build/appicon.png and b/build/appicon.png differBinary files a/build/appicon.png and b/build/appicon.png differ
added build/appicon.svg +15 -0
new file mode 100644
@@ -0,0 +1,15 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
3+ <rect x="100" y="100" width="824" height="824" rx="185" fill="#1E2240"/>
4+ <svg x="212" y="212" width="600" height="600" viewBox="0 0 100 100">
5+ <mask id="cut">
6+ <rect width="100" height="100" fill="#fff"/>
7+ <circle cx="76" cy="28" r="12" fill="#000"/>
8+ <circle cx="18" cy="39" r="10" fill="#000"/>
9+ </mask>
10+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
11+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
12+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
13+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
14+ </svg>
15+</svg>
new file mode 100644
@@ -0,0 +1,15 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
3+ <rect x="100" y="100" width="824" height="824" rx="185" fill="#1E2240"/>
4+ <svg x="212" y="212" width="600" height="600" viewBox="0 0 100 100">
5+ <mask id="cut">
6+ <rect width="100" height="100" fill="#fff"/>
7+ <circle cx="76" cy="28" r="12" fill="#000"/>
8+ <circle cx="18" cy="39" r="10" fill="#000"/>
9+ </mask>
10+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
11+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
12+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
13+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
14+ </svg>
15+</svg>
modified build/config.yml +1 -1
@@ -11,7 +11,7 @@ info:
1111 description: "Converse with AI agents in channels" # The application description
1212 copyright: "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0" # Copyright text
1313 comments: "Sidekicks" # Comments
14- version: "0.0.1" # The application version
14+ version: "0.1.0" # The application version
1515 # cfBundleIconName: "appicon" # The macOS icon name in Assets.car icon bundles (optional)
1616 # # Should match the name of your .icon file without the extension
1717 # # If not set and Assets.car exists, defaults to "appicon"
@@ -11,7 +11,7 @@ info:
11 description: "Converse with AI agents in channels" # The application description11 description: "Converse with AI agents in channels" # The application description
12 copyright: "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0" # Copyright text12 copyright: "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0" # Copyright text
13 comments: "Sidekicks" # Comments13 comments: "Sidekicks" # Comments
14- version: "0.0.1" # The application version14+ version: "0.1.0" # The application version
15 # cfBundleIconName: "appicon" # The macOS icon name in Assets.car icon bundles (optional)15 # cfBundleIconName: "appicon" # The macOS icon name in Assets.car icon bundles (optional)
16 # # Should match the name of your .icon file without the extension16 # # Should match the name of your .icon file without the extension
17 # # If not set and Assets.car exists, defaults to "appicon"17 # # If not set and Assets.car exists, defaults to "appicon"
modified build/darwin/Assets.car +0 -0
Binary files a/build/darwin/Assets.car and b/build/darwin/Assets.car differ
Binary files a/build/darwin/Assets.car and b/build/darwin/Assets.car differBinary files a/build/darwin/Assets.car and b/build/darwin/Assets.car differ
modified build/darwin/Info.dev.plist +32 -31
@@ -1,34 +1,35 @@
1+<?xml version="1.0" encoding="UTF-8"?>
12 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
23 <plist version="1.0">
3- <dict>
4- <key>CFBundlePackageType</key>
5- <string>APPL</string>
6- <key>CFBundleName</key>
7- <string>Sidekicks</string>
8- <key>CFBundleExecutable</key>
9- <string>sidekicks</string>
10- <key>CFBundleIdentifier</key>
11- <string>com.k33g.sidekicks</string>
12- <key>CFBundleVersion</key>
13- <string>0.0.1</string>
14- <key>CFBundleGetInfoString</key>
15- <string>This is a comment</string>
16- <key>CFBundleShortVersionString</key>
17- <string>0.0.1</string>
18- <key>CFBundleIconFile</key>
19- <string>icons</string>
20- <key>CFBundleIconName</key>
21- <string>appicon</string>
22- <key>LSMinimumSystemVersion</key>
23- <string>12.0.0</string>
24- <key>NSHighResolutionCapable</key>
25- <string>true</string>
26- <key>NSHumanReadableCopyright</key>
27- <string>© 2026 Philippe Charrière</string>
28- <key>NSAppTransportSecurity</key>
29- <dict>
30- <key>NSAllowsLocalNetworking</key>
31- <true/>
32- </dict>
33- </dict>
4+ <dict>
5+ <key>CFBundleExecutable</key>
6+ <string>sidekicks</string>
7+ <key>CFBundleGetInfoString</key>
8+ <string>Sidekicks</string>
9+ <key>CFBundleIconFile</key>
10+ <string>icons</string>
11+ <key>CFBundleIconName</key>
12+ <string>appicon</string>
13+ <key>CFBundleIdentifier</key>
14+ <string>com.k33g.sidekicks</string>
15+ <key>CFBundleName</key>
16+ <string>Sidekicks</string>
17+ <key>CFBundlePackageType</key>
18+ <string>APPL</string>
19+ <key>CFBundleShortVersionString</key>
20+ <string>0.1.0</string>
21+ <key>CFBundleVersion</key>
22+ <string>0.1.0</string>
23+ <key>LSMinimumSystemVersion</key>
24+ <string>13.0.0</string>
25+ <key>NSAppTransportSecurity</key>
26+ <dict>
27+ <key>NSAllowsLocalNetworking</key>
28+ <true/>
29+ </dict>
30+ <key>NSHighResolutionCapable</key>
31+ <string>true</string>
32+ <key>NSHumanReadableCopyright</key>
33+ <string>© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0</string>
34+ </dict>
3435 </plist>
\ No newline at end of file
@@ -1,34 +1,35 @@
1+<?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2 <plist version="1.0">3 <plist version="1.0">
3- <dict>4+ <dict>
4- <key>CFBundlePackageType</key>5+ <key>CFBundleExecutable</key>
5- <string>APPL</string>6+ <string>sidekicks</string>
6- <key>CFBundleName</key>7+ <key>CFBundleGetInfoString</key>
7- <string>Sidekicks</string>8+ <string>Sidekicks</string>
8- <key>CFBundleExecutable</key>9+ <key>CFBundleIconFile</key>
9- <string>sidekicks</string>10+ <string>icons</string>
10- <key>CFBundleIdentifier</key>11+ <key>CFBundleIconName</key>
11- <string>com.k33g.sidekicks</string>12+ <string>appicon</string>
12- <key>CFBundleVersion</key>13+ <key>CFBundleIdentifier</key>
13- <string>0.0.1</string>14+ <string>com.k33g.sidekicks</string>
14- <key>CFBundleGetInfoString</key>15+ <key>CFBundleName</key>
15- <string>This is a comment</string>16+ <string>Sidekicks</string>
16- <key>CFBundleShortVersionString</key>17+ <key>CFBundlePackageType</key>
17- <string>0.0.1</string>18+ <string>APPL</string>
18- <key>CFBundleIconFile</key>19+ <key>CFBundleShortVersionString</key>
19- <string>icons</string>20+ <string>0.1.0</string>
20- <key>CFBundleIconName</key>21+ <key>CFBundleVersion</key>
21- <string>appicon</string>22+ <string>0.1.0</string>
22- <key>LSMinimumSystemVersion</key>23+ <key>LSMinimumSystemVersion</key>
23- <string>12.0.0</string>24+ <string>13.0.0</string>
24- <key>NSHighResolutionCapable</key>25+ <key>NSAppTransportSecurity</key>
25- <string>true</string>26+ <dict>
26- <key>NSHumanReadableCopyright</key>27+ <key>NSAllowsLocalNetworking</key>
27- <string>© 2026 Philippe Charrière</string>28+ <true/>
28- <key>NSAppTransportSecurity</key>29+ </dict>
29- <dict>30+ <key>NSHighResolutionCapable</key>
30- <key>NSAllowsLocalNetworking</key>31+ <string>true</string>
31- <true/>32+ <key>NSHumanReadableCopyright</key>
32- </dict>33+ <string>© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0</string>
33- </dict>34+ </dict>
34 </plist>35 </plist>
\ No newline at end of file\ No newline at end of file
modified build/darwin/Info.plist +27 -26
@@ -1,29 +1,30 @@
1+<?xml version="1.0" encoding="UTF-8"?>
12 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
23 <plist version="1.0">
3- <dict>
4- <key>CFBundlePackageType</key>
5- <string>APPL</string>
6- <key>CFBundleName</key>
7- <string>Sidekicks</string>
8- <key>CFBundleExecutable</key>
9- <string>sidekicks</string>
10- <key>CFBundleIdentifier</key>
11- <string>com.k33g.sidekicks</string>
12- <key>CFBundleVersion</key>
13- <string>0.0.1</string>
14- <key>CFBundleGetInfoString</key>
15- <string>This is a comment</string>
16- <key>CFBundleShortVersionString</key>
17- <string>0.0.1</string>
18- <key>CFBundleIconFile</key>
19- <string>icons</string>
20- <key>CFBundleIconName</key>
21- <string>appicon</string>
22- <key>LSMinimumSystemVersion</key>
23- <string>12.0.0</string>
24- <key>NSHighResolutionCapable</key>
25- <string>true</string>
26- <key>NSHumanReadableCopyright</key>
27- <string>© 2026 Philippe Charrière</string>
28- </dict>
4+ <dict>
5+ <key>CFBundleExecutable</key>
6+ <string>sidekicks</string>
7+ <key>CFBundleGetInfoString</key>
8+ <string>Sidekicks</string>
9+ <key>CFBundleIconFile</key>
10+ <string>icons</string>
11+ <key>CFBundleIconName</key>
12+ <string>appicon</string>
13+ <key>CFBundleIdentifier</key>
14+ <string>com.k33g.sidekicks</string>
15+ <key>CFBundleName</key>
16+ <string>Sidekicks</string>
17+ <key>CFBundlePackageType</key>
18+ <string>APPL</string>
19+ <key>CFBundleShortVersionString</key>
20+ <string>0.1.0</string>
21+ <key>CFBundleVersion</key>
22+ <string>0.1.0</string>
23+ <key>LSMinimumSystemVersion</key>
24+ <string>13.0.0</string>
25+ <key>NSHighResolutionCapable</key>
26+ <string>true</string>
27+ <key>NSHumanReadableCopyright</key>
28+ <string>© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0</string>
29+ </dict>
2930 </plist>
\ No newline at end of file
@@ -1,29 +1,30 @@
1+<?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2 <plist version="1.0">3 <plist version="1.0">
3- <dict>4+ <dict>
4- <key>CFBundlePackageType</key>5+ <key>CFBundleExecutable</key>
5- <string>APPL</string>6+ <string>sidekicks</string>
6- <key>CFBundleName</key>7+ <key>CFBundleGetInfoString</key>
7- <string>Sidekicks</string>8+ <string>Sidekicks</string>
8- <key>CFBundleExecutable</key>9+ <key>CFBundleIconFile</key>
9- <string>sidekicks</string>10+ <string>icons</string>
10- <key>CFBundleIdentifier</key>11+ <key>CFBundleIconName</key>
11- <string>com.k33g.sidekicks</string>12+ <string>appicon</string>
12- <key>CFBundleVersion</key>13+ <key>CFBundleIdentifier</key>
13- <string>0.0.1</string>14+ <string>com.k33g.sidekicks</string>
14- <key>CFBundleGetInfoString</key>15+ <key>CFBundleName</key>
15- <string>This is a comment</string>16+ <string>Sidekicks</string>
16- <key>CFBundleShortVersionString</key>17+ <key>CFBundlePackageType</key>
17- <string>0.0.1</string>18+ <string>APPL</string>
18- <key>CFBundleIconFile</key>19+ <key>CFBundleShortVersionString</key>
19- <string>icons</string>20+ <string>0.1.0</string>
20- <key>CFBundleIconName</key>21+ <key>CFBundleVersion</key>
21- <string>appicon</string>22+ <string>0.1.0</string>
22- <key>LSMinimumSystemVersion</key>23+ <key>LSMinimumSystemVersion</key>
23- <string>12.0.0</string>24+ <string>13.0.0</string>
24- <key>NSHighResolutionCapable</key>25+ <key>NSHighResolutionCapable</key>
25- <string>true</string>26+ <string>true</string>
26- <key>NSHumanReadableCopyright</key>27+ <key>NSHumanReadableCopyright</key>
27- <string>© 2026 Philippe Charrière</string>28+ <string>© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0</string>
28- </dict>29+ </dict>
29 </plist>30 </plist>
\ No newline at end of file\ No newline at end of file
modified build/darwin/Taskfile.yml +3 -3
@@ -51,9 +51,9 @@ tasks:
5151 GOOS: darwin
5252 CGO_ENABLED: 1
5353 GOARCH: '{{.ARCH | default ARCH}}'
54- CGO_CFLAGS: "-mmacosx-version-min=12.0"
55- CGO_LDFLAGS: "-mmacosx-version-min=12.0"
56- MACOSX_DEPLOYMENT_TARGET: "12.0"
54+ CGO_CFLAGS: "-mmacosx-version-min=13.0"
55+ CGO_LDFLAGS: "-mmacosx-version-min=13.0"
56+ MACOSX_DEPLOYMENT_TARGET: "13.0"
5757
5858 build:docker:
5959 summary: Cross-compiles for macOS using Docker (for Linux/Windows hosts)
@@ -51,9 +51,9 @@ tasks:
51 GOOS: darwin51 GOOS: darwin
52 CGO_ENABLED: 152 CGO_ENABLED: 1
53 GOARCH: '{{.ARCH | default ARCH}}'53 GOARCH: '{{.ARCH | default ARCH}}'
54- CGO_CFLAGS: "-mmacosx-version-min=12.0"54+ CGO_CFLAGS: "-mmacosx-version-min=13.0"
55- CGO_LDFLAGS: "-mmacosx-version-min=12.0"55+ CGO_LDFLAGS: "-mmacosx-version-min=13.0"
56- MACOSX_DEPLOYMENT_TARGET: "12.0"56+ MACOSX_DEPLOYMENT_TARGET: "13.0"
57 57
58 build:docker:58 build:docker:
59 summary: Cross-compiles for macOS using Docker (for Linux/Windows hosts)59 summary: Cross-compiles for macOS using Docker (for Linux/Windows hosts)
modified build/darwin/icons.icns +0 -0
Binary files a/build/darwin/icons.icns and b/build/darwin/icons.icns differ
Binary files a/build/darwin/icons.icns and b/build/darwin/icons.icns differBinary files a/build/darwin/icons.icns and b/build/darwin/icons.icns differ
modified build/linux/Taskfile.yml +3 -1
@@ -170,7 +170,9 @@ tasks:
170170 dir: build
171171 cmds:
172172 - mkdir -p {{.ROOT_DIR}}/build/linux/appimage
173- - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile "{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop" -categories "{{.CATEGORIES}}"
173+ # Sidekicks keeps its own desktop entry (build/linux/desktop): the
174+ # generated one names the application in lowercase, as a development tool.
175+ - cp "{{.ROOT_DIR}}/build/linux/desktop" "{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop"
174176 vars:
175177 APP_NAME: '{{.APP_NAME}}'
176178 EXEC: '{{.APP_NAME}}'
@@ -170,7 +170,9 @@ tasks:
170 dir: build170 dir: build
171 cmds:171 cmds:
172 - mkdir -p {{.ROOT_DIR}}/build/linux/appimage172 - mkdir -p {{.ROOT_DIR}}/build/linux/appimage
173- - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile "{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop" -categories "{{.CATEGORIES}}"173+ # Sidekicks keeps its own desktop entry (build/linux/desktop): the
174+ # generated one names the application in lowercase, as a development tool.
175+ - cp "{{.ROOT_DIR}}/build/linux/desktop" "{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop"
174 vars:176 vars:
175 APP_NAME: '{{.APP_NAME}}'177 APP_NAME: '{{.APP_NAME}}'
176 EXEC: '{{.APP_NAME}}'178 EXEC: '{{.APP_NAME}}'
modified build/linux/desktop +1 -4
@@ -2,12 +2,9 @@
22 Version=1.0
33 Name=Sidekicks
44 Comment=Converse with AI agents in channels
5-# The Exec line includes %u to pass the URL to the application
6-Exec=/usr/local/bin/sidekicks %u
5+Exec=sidekicks
76 Terminal=false
87 Type=Application
98 Icon=sidekicks
109 Categories=Utility;
1110 StartupWMClass=sidekicks
12-
13-
@@ -2,12 +2,9 @@
2 Version=1.02 Version=1.0
3 Name=Sidekicks3 Name=Sidekicks
4 Comment=Converse with AI agents in channels4 Comment=Converse with AI agents in channels
5-# The Exec line includes %u to pass the URL to the application5+Exec=sidekicks
6-Exec=/usr/local/bin/sidekicks %u
7 Terminal=false6 Terminal=false
8 Type=Application7 Type=Application
9 Icon=sidekicks8 Icon=sidekicks
10 Categories=Utility;9 Categories=Utility;
11 StartupWMClass=sidekicks10 StartupWMClass=sidekicks
12-
13-
modified build/linux/nfpm/nfpm.yaml +7 -5
@@ -6,21 +6,23 @@
66 name: "sidekicks"
77 arch: ${GOARCH}
88 platform: "linux"
9-version: "0.0.1"
9+version: "0.1.0"
1010 section: "default"
1111 priority: "extra"
12-maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
12+maintainer: "Philippe Charrière"
1313 description: "Converse with AI agents in channels"
1414 vendor: "Philippe Charrière"
15-homepage: "https://wails.io"
16-license: "MIT"
15+homepage: "https://git.rickub.com/bots-garden/sidekicks"
16+license: "LicenseRef-Sidekicks-Source-Available-1.0"
1717 release: "1"
1818
1919 contents:
2020 - src: "./bin/sidekicks"
2121 dst: "/usr/local/bin/sidekicks"
22+ - src: "./build/appicon.svg"
23+ dst: "/usr/share/icons/hicolor/scalable/apps/sidekicks.svg"
2224 - src: "./build/appicon.png"
23- dst: "/usr/share/icons/hicolor/128x128/apps/sidekicks.png"
25+ dst: "/usr/share/pixmaps/sidekicks.png"
2426 - src: "./build/linux/sidekicks.desktop"
2527 dst: "/usr/share/applications/sidekicks.desktop"
2628
@@ -6,21 +6,23 @@
6 name: "sidekicks"6 name: "sidekicks"
7 arch: ${GOARCH}7 arch: ${GOARCH}
8 platform: "linux"8 platform: "linux"
9-version: "0.0.1"9+version: "0.1.0"
10 section: "default"10 section: "default"
11 priority: "extra"11 priority: "extra"
12-maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>12+maintainer: "Philippe Charrière"
13 description: "Converse with AI agents in channels"13 description: "Converse with AI agents in channels"
14 vendor: "Philippe Charrière"14 vendor: "Philippe Charrière"
15-homepage: "https://wails.io"15+homepage: "https://git.rickub.com/bots-garden/sidekicks"
16-license: "MIT"16+license: "LicenseRef-Sidekicks-Source-Available-1.0"
17 release: "1"17 release: "1"
18 18
19 contents:19 contents:
20 - src: "./bin/sidekicks"20 - src: "./bin/sidekicks"
21 dst: "/usr/local/bin/sidekicks"21 dst: "/usr/local/bin/sidekicks"
22+ - src: "./build/appicon.svg"
23+ dst: "/usr/share/icons/hicolor/scalable/apps/sidekicks.svg"
22 - src: "./build/appicon.png"24 - src: "./build/appicon.png"
23- dst: "/usr/share/icons/hicolor/128x128/apps/sidekicks.png"25+ dst: "/usr/share/pixmaps/sidekicks.png"
24 - src: "./build/linux/sidekicks.desktop"26 - src: "./build/linux/sidekicks.desktop"
25 dst: "/usr/share/applications/sidekicks.desktop"27 dst: "/usr/share/applications/sidekicks.desktop"
26 28
modified build/windows/icon.ico +0 -0
Binary files a/build/windows/icon.ico and b/build/windows/icon.ico differ
Binary files a/build/windows/icon.ico and b/build/windows/icon.ico differBinary files a/build/windows/icon.ico and b/build/windows/icon.ico differ
modified build/windows/info.json +4 -4
@@ -1,15 +1,15 @@
11 {
22 "fixed": {
3- "file_version": "0.0.1"
3+ "file_version": "0.1.0"
44 },
55 "info": {
66 "0409": {
7- "ProductVersion": "0.0.1",
7+ "ProductVersion": "0.1.0",
88 "CompanyName": "Philippe Charrière",
99 "FileDescription": "Converse with AI agents in channels",
10- "LegalCopyright": "© 2026 Philippe Charrière",
10+ "LegalCopyright": "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0",
1111 "ProductName": "Sidekicks",
12- "Comments": "This is a comment"
12+ "Comments": "Sidekicks"
1313 }
1414 }
1515 }
\ No newline at end of file
@@ -1,15 +1,15 @@
1 {1 {
2 "fixed": {2 "fixed": {
3- "file_version": "0.0.1"3+ "file_version": "0.1.0"
4 },4 },
5 "info": {5 "info": {
6 "0409": {6 "0409": {
7- "ProductVersion": "0.0.1",7+ "ProductVersion": "0.1.0",
8 "CompanyName": "Philippe Charrière",8 "CompanyName": "Philippe Charrière",
9 "FileDescription": "Converse with AI agents in channels",9 "FileDescription": "Converse with AI agents in channels",
10- "LegalCopyright": "© 2026 Philippe Charrière",10+ "LegalCopyright": "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0",
11 "ProductName": "Sidekicks",11 "ProductName": "Sidekicks",
12- "Comments": "This is a comment"12+ "Comments": "Sidekicks"
13 }13 }
14 }14 }
15 }15 }
\ No newline at end of file\ No newline at end of file
modified build/windows/nsis/wails_tools.nsh +2 -2
@@ -14,10 +14,10 @@
1414 !define INFO_PRODUCTNAME "Sidekicks"
1515 !endif
1616 !ifndef INFO_PRODUCTVERSION
17- !define INFO_PRODUCTVERSION "0.0.1"
17+ !define INFO_PRODUCTVERSION "0.1.0"
1818 !endif
1919 !ifndef INFO_COPYRIGHT
20- !define INFO_COPYRIGHT "© 2026 Philippe Charrière"
20+ !define INFO_COPYRIGHT "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0"
2121 !endif
2222 !ifndef PRODUCT_EXECUTABLE
2323 !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
@@ -14,10 +14,10 @@
14 !define INFO_PRODUCTNAME "Sidekicks"14 !define INFO_PRODUCTNAME "Sidekicks"
15 !endif15 !endif
16 !ifndef INFO_PRODUCTVERSION16 !ifndef INFO_PRODUCTVERSION
17- !define INFO_PRODUCTVERSION "0.0.1"17+ !define INFO_PRODUCTVERSION "0.1.0"
18 !endif18 !endif
19 !ifndef INFO_COPYRIGHT19 !ifndef INFO_COPYRIGHT
20- !define INFO_COPYRIGHT "© 2026 Philippe Charrière"20+ !define INFO_COPYRIGHT "© 2026 Philippe Charrière — Sidekicks Source-Available License 1.0"
21 !endif21 !endif
22 !ifndef PRODUCT_EXECUTABLE22 !ifndef PRODUCT_EXECUTABLE
23 !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"23 !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
modified build/windows/wails.exe.manifest +1 -1
@@ -1,6 +1,6 @@
11 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22 <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
3- <assemblyIdentity type="win32" name="com.k33g.sidekicks" version="0.0.1" processorArchitecture="*"/>
3+ <assemblyIdentity type="win32" name="com.k33g.sidekicks" version="0.1.0" processorArchitecture="*"/>
44 <dependency>
55 <dependentAssembly>
66 <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
@@ -1,6 +1,6 @@
1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">2 <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
3- <assemblyIdentity type="win32" name="com.k33g.sidekicks" version="0.0.1" processorArchitecture="*"/>3+ <assemblyIdentity type="win32" name="com.k33g.sidekicks" version="0.1.0" processorArchitecture="*"/>
4 <dependency>4 <dependency>
5 <dependentAssembly>5 <dependentAssembly>
6 <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>6 <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
modified docs/design/01-layout.png +0 -0
Binary files a/docs/design/01-layout.png and b/docs/design/01-layout.png differ
Binary files a/docs/design/01-layout.png and b/docs/design/01-layout.png differBinary files a/docs/design/01-layout.png and b/docs/design/01-layout.png differ
modified docs/design/01-layout.svg +33 -33
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">1 · Layout global</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Docks façon Zed, mais seul l'essentiel est visible par défaut</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">1 · Overall layout</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Zed-like docks, but only the essentials are visible by default</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
1616 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1717 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
1818 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
1919 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>
20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
2222 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
2323 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
2424 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -30,18 +30,18 @@
3030 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
3131 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
3232 <rect x="251" y="116" width="709" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
33- <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>
33+ <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
3434 <text x="944" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob</text>
35- <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>
36- <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice résume-moi ce que fait ACP en deux phrases</text>
35+ <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
36+ <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice sum up what ACP does in two sentences</text>
3737 <text x="275" y="252" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>
38- <text x="275" y="274.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">ACP est un protocole qui permet à une application de dialoguer avec</text>
39- <text x="275" y="293.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique.</text>
38+ <text x="275" y="274.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">ACP is a protocol that lets an application talk with any AI agent.</text>
39+ <text x="275" y="293.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Sidekicks uses it to stay agent-agnostic.</text>
4040 <rect x="271" y="724" width="669" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
41- <text x="285" y="750" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">Écrivez un message… (@ pour un agent, / pour une commande)</text>
42- <text x="285" y="774" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Entrée pour envoyer · Maj+Entrée nouvelle ligne</text>
41+ <text x="285" y="750" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">Write a message… (@ for an agent, / for a command)</text>
42+ <text x="285" y="774" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Enter to send · Shift+Enter for a new line</text>
4343 <rect x="960" y="116" width="300" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
44- <text x="976" y="144" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">⚙︎ Settings — equipe.yaml</text>
44+ <text x="976" y="144" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">⚙︎ Settings — team.yaml</text>
4545 <rect x="970" y="160" width="280" height="300" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
4646 <text x="980" y="184" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">version: 1</text>
4747 <text x="980" y="203" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">agents:</text>
@@ -50,13 +50,13 @@
5050 <text x="980" y="260" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> command: mm</text>
5151 <text x="980" y="279" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> args: [-acp, ...]</text>
5252 <text x="980" y="298" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">channels:</text>
53- <text x="980" y="317" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> - name: général</text>
53+ <text x="980" y="317" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> - name: general</text>
5454 <text x="980" y="336" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> agents: [alice, bob]</text>
55- <text x="976" y="490" font-size="12.5" fill="#3E9B4F" text-anchor="start" xml:space="preserve" font-weight="700">✓ Configuration valide</text>
55+ <text x="976" y="490" font-size="12.5" fill="#3E9B4F" text-anchor="start" xml:space="preserve" font-weight="700">✓ Valid configuration</text>
5656 <rect x="970" y="508" width="120" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
57- <text x="1030.0" y="529.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Enregistrer</text>
57+ <text x="1030.0" y="529.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Save</text>
5858 <rect x="251" y="804" width="709" height="30" rx="0" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
59- <text x="265" y="824" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Terminal · Fichiers (repliés)</text>
59+ <text x="265" y="824" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Terminal · Files (folded)</text>
6060 <circle cx="220" cy="142" r="12" fill="#6A1B9A"/>
6161 <text x="220" y="146.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
6262 <circle cx="371" cy="240" r="12" fill="#6A1B9A"/>
@@ -70,32 +70,32 @@
7070 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7171 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
7272 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
73- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock gauche : channels et agents</text>
74- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Toujours visible. La pastille donne l'état de</text>
75- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent : vert prêt, orange occupé, gris arrêté.</text>
73+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Left dock: channels and agents</text>
74+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Always visible. The dot gives the agent's</text>
75+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">state: green ready, orange busy, grey stopped.</text>
7676 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7777 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
7878 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
79- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Centre : la conversation</text>
80- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le seul panel indispensable. Pleine largeur</text>
81- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">quand les docks sont repliés.</text>
79+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Centre: the conversation</text>
80+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The only panel you cannot do without.</text>
81+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Full width when the docks are folded.</text>
8282 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8383 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
8484 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
85- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock droit : Settings</text>
86- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Éditeur YAML (CodeMirror) avec validation</text>
87- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">en direct. Fermé par défaut ; s'ouvre</text>
88- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">par ⚙︎ ou ⌘,.</text>
85+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Right dock: Settings</text>
86+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">YAML editor (CodeMirror) with live</text>
87+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">validation. Closed by default; opens</text>
88+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">with ⚙︎ or ⌘,.</text>
8989 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9090 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>
9191 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
92- <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock bas : terminal et fichiers</text>
93- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Replié par défaut : utile aux curieux,</text>
94- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">jamais imposé au citizen user.</text>
92+ <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Bottom dock: terminal and files</text>
93+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Folded by default: there for the curious,</text>
94+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">never forced on the citizen user.</text>
9595 <rect x="1284" y="458" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9696 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>
9797 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
98- <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Barre d'état</text>
99- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Rappelle la limite d'agents simultanés</text>
100- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">(mémoire) et la configuration ouverte.</text>
98+ <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Status bar</text>
99+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Shows the limit of agents running at once</text>
100+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">(memory) and the open configuration.</text>
101101 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">1 · Layout global</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">1 · Overall layout</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Docks façon Zed, mais seul l'essentiel est visible par défaut</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Zed-like docks, but only the essentials are visible by default</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -30,18 +30,18 @@
30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
32 <rect x="251" y="116" width="709" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>32 <rect x="251" y="116" width="709" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
33- <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>33+ <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
34 <text x="944" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob</text>34 <text x="944" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob</text>
35- <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>35+ <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
36- <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice résume-moi ce que fait ACP en deux phrases</text>36+ <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice sum up what ACP does in two sentences</text>
37 <text x="275" y="252" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>37 <text x="275" y="252" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>
38- <text x="275" y="274.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">ACP est un protocole qui permet à une application de dialoguer avec</text>38+ <text x="275" y="274.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">ACP is a protocol that lets an application talk with any AI agent.</text>
39- <text x="275" y="293.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique.</text>39+ <text x="275" y="293.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Sidekicks uses it to stay agent-agnostic.</text>
40 <rect x="271" y="724" width="669" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>40 <rect x="271" y="724" width="669" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
41- <text x="285" y="750" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">Écrivez un message… (@ pour un agent, / pour une commande)</text>41+ <text x="285" y="750" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">Write a message… (@ for an agent, / for a command)</text>
42- <text x="285" y="774" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Entrée pour envoyer · Maj+Entrée nouvelle ligne</text>42+ <text x="285" y="774" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Enter to send · Shift+Enter for a new line</text>
43 <rect x="960" y="116" width="300" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>43 <rect x="960" y="116" width="300" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
44- <text x="976" y="144" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">⚙︎ Settings — equipe.yaml</text>44+ <text x="976" y="144" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">⚙︎ Settings — team.yaml</text>
45 <rect x="970" y="160" width="280" height="300" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>45 <rect x="970" y="160" width="280" height="300" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
46 <text x="980" y="184" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">version: 1</text>46 <text x="980" y="184" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">version: 1</text>
47 <text x="980" y="203" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">agents:</text>47 <text x="980" y="203" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">agents:</text>
@@ -50,13 +50,13 @@
50 <text x="980" y="260" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> command: mm</text>50 <text x="980" y="260" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> command: mm</text>
51 <text x="980" y="279" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> args: [-acp, ...]</text>51 <text x="980" y="279" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> args: [-acp, ...]</text>
52 <text x="980" y="298" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">channels:</text>52 <text x="980" y="298" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">channels:</text>
53- <text x="980" y="317" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> - name: général</text>53+ <text x="980" y="317" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> - name: general</text>
54 <text x="980" y="336" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> agents: [alice, bob]</text>54 <text x="980" y="336" font-size="12" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> agents: [alice, bob]</text>
55- <text x="976" y="490" font-size="12.5" fill="#3E9B4F" text-anchor="start" xml:space="preserve" font-weight="700">✓ Configuration valide</text>55+ <text x="976" y="490" font-size="12.5" fill="#3E9B4F" text-anchor="start" xml:space="preserve" font-weight="700">✓ Valid configuration</text>
56 <rect x="970" y="508" width="120" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>56 <rect x="970" y="508" width="120" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
57- <text x="1030.0" y="529.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Enregistrer</text>57+ <text x="1030.0" y="529.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Save</text>
58 <rect x="251" y="804" width="709" height="30" rx="0" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>58 <rect x="251" y="804" width="709" height="30" rx="0" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
59- <text x="265" y="824" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Terminal · Fichiers (repliés)</text>59+ <text x="265" y="824" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Terminal · Files (folded)</text>
60 <circle cx="220" cy="142" r="12" fill="#6A1B9A"/>60 <circle cx="220" cy="142" r="12" fill="#6A1B9A"/>
61 <text x="220" y="146.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>61 <text x="220" y="146.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
62 <circle cx="371" cy="240" r="12" fill="#6A1B9A"/>62 <circle cx="371" cy="240" r="12" fill="#6A1B9A"/>
@@ -70,32 +70,32 @@
70 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>70 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
71 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>71 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
72 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>72 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
73- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock gauche : channels et agents</text>73+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Left dock: channels and agents</text>
74- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Toujours visible. La pastille donne l'état de</text>74+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Always visible. The dot gives the agent's</text>
75- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent : vert prêt, orange occupé, gris arrêté.</text>75+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">state: green ready, orange busy, grey stopped.</text>
76 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>76 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
77 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>77 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
78 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>78 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
79- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Centre : la conversation</text>79+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Centre: the conversation</text>
80- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le seul panel indispensable. Pleine largeur</text>80+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The only panel you cannot do without.</text>
81- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">quand les docks sont repliés.</text>81+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Full width when the docks are folded.</text>
82 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>82 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
83 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>83 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
84 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>84 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
85- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock droit : Settings</text>85+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Right dock: Settings</text>
86- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Éditeur YAML (CodeMirror) avec validation</text>86+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">YAML editor (CodeMirror) with live</text>
87- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">en direct. Fermé par défaut ; s'ouvre</text>87+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">validation. Closed by default; opens</text>
88- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">par ⚙︎ ou ⌘,.</text>88+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">with ⚙︎ or ⌘,.</text>
89 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>89 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
90 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>90 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>
91 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>91 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
92- <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Dock bas : terminal et fichiers</text>92+ <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Bottom dock: terminal and files</text>
93- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Replié par défaut : utile aux curieux,</text>93+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Folded by default: there for the curious,</text>
94- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">jamais imposé au citizen user.</text>94+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">never forced on the citizen user.</text>
95 <rect x="1284" y="458" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>95 <rect x="1284" y="458" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
96 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>96 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>
97 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>97 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
98- <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Barre d'état</text>98+ <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Status bar</text>
99- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Rappelle la limite d'agents simultanés</text>99+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Shows the limit of agents running at once</text>
100- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">(mémoire) et la configuration ouverte.</text>100+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">(memory) and the open configuration.</text>
101 </svg>101 </svg>
modified docs/design/02-conversation.png +0 -0
Binary files a/docs/design/02-conversation.png and b/docs/design/02-conversation.png differ
Binary files a/docs/design/02-conversation.png and b/docs/design/02-conversation.png differBinary files a/docs/design/02-conversation.png and b/docs/design/02-conversation.png differ
modified docs/design/02-conversation.svg +37 -37
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">2 · Conversation multi-agents</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">2 · Multi-agent conversation</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Each agent has its colour; tools are folded; you can always stop</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
1616 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1717 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
1818 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
1919 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>
20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
2222 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
2323 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
2424 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -30,31 +30,31 @@
3030 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
3131 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
3232 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
33- <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>
33+ <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
3434 <text x="1244" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob · 🧑‍🚀 Riker</text>
35- <text x="275" y="196" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">— Riker a rejoint le channel —</text>
36- <text x="275" y="228" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>
37- <text x="275" y="250.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice propose un nom pour un script qui sauvegarde mes photos</text>
35+ <text x="275" y="196" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">— Riker joined the channel —</text>
36+ <text x="275" y="228" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
37+ <text x="275" y="250.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice suggest a name for a script that backs up my photos</text>
3838 <text x="275" y="284" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>
39- <text x="275" y="306.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Je propose « photo-backup.sh » : court, explicite, et l'extension dit que</text>
40- <text x="275" y="325.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">c'est un script shell.</text>
41- <text x="275" y="360" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>
42- <text x="275" y="382.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@bob tu valides ? @riker crée le fichier vide</text>
39+ <text x="275" y="306.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">I suggest "photo-backup.sh": short, clear, and the extension says</text>
40+ <text x="275" y="325.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">it is a shell script.</text>
41+ <text x="275" y="360" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
42+ <text x="275" y="382.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@bob do you agree? @riker create the empty file</text>
4343 <text x="275" y="416" font-size="13.5" fill="#00838F" text-anchor="start" xml:space="preserve" font-weight="700">🧔 Bob</text>
44- <text x="275" y="438.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Bon choix. Alice a raison : un nom en minuscules, sans espace.</text>
44+ <text x="275" y="438.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Good choice. Alice is right: a lowercase name, no spaces.</text>
4545 <text x="275" y="472" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>
46- <text x="365" y="472" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">travaille…</text>
46+ <text x="365" y="472" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">working…</text>
4747 <rect x="275" y="484" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
48- <text x="289" y="509" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé</text>
48+ <text x="289" y="509" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done</text>
4949 <rect x="275" y="532" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
50- <text x="289" y="557" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 bash : ls -la … en cours</text>
50+ <text x="289" y="557" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 bash: ls -la … running</text>
5151 <rect x="271" y="754" width="849" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
5252 <text x="285" y="780" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve"></text>
53- <text x="285" y="804" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Entrée pour envoyer · Maj+Entrée nouvelle ligne</text>
53+ <text x="285" y="804" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Enter to send · Shift+Enter for a new line</text>
5454 <rect x="1132" y="770" width="108" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
55- <text x="1186.0" y="791.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Arrêter</text>
55+ <text x="1186.0" y="791.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Stop</text>
5656 <rect x="1040" y="176" width="200" height="30" rx="15" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
57- <text x="1140" y="196" font-size="11.5" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">contexte Riker : 41 %</text>
57+ <text x="1140" y="196" font-size="11.5" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Riker's context: 41%</text>
5858 <circle cx="575" cy="191" r="12" fill="#6A1B9A"/>
5959 <text x="575" y="195.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
6060 <circle cx="355" cy="274" r="12" fill="#6A1B9A"/>
@@ -68,33 +68,33 @@
6868 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
6969 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
7070 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
71- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Arrivée visible</text>
72- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">La 1re mention invite l'agent : un message</text>
73- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">système le dit. On sait qui « écoute ».</text>
71+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">A visible arrival</text>
72+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The first mention invites the agent: a system</text>
73+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">message says so. You know who is listening.</text>
7474 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7575 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
7676 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
77- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Une couleur par agent</text>
78- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Avatar + nom colorés, réponses en Markdown</text>
79- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">avec code coloré (ADR 0002).</text>
77+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">One colour per agent</text>
78+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Coloured avatar and name, answers in</text>
79+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Markdown with highlighted code (ADR 0002).</text>
8080 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8181 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
8282 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
83- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Outils repliés</text>
84- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Une ligne par tool call : titre lisible et</text>
85- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">statut. Déplier montre la commande, le</text>
86- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">diff ou la sortie.</text>
83+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Folded tools</text>
84+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">One line per tool call: a readable title</text>
85+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">and its status. Unfolding shows the</text>
86+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">command, the diff or the output.</text>
8787 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8888 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>
8989 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
90- <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Toujours un bouton Arrêter</text>
91- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Envoie session/cancel aux agents qui</text>
92- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">répondent dans ce channel (ADR 0001).</text>
90+ <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Always a Stop button</text>
91+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Sends session/cancel to the agents</text>
92+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">answering in this channel (ADR 0001).</text>
9393 <rect x="1284" y="458" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9494 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>
9595 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
96- <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Jauge de contexte</text>
97- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">usage_update : prévient avant que la</text>
98- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">conversation ne dépasse la mémoire de</text>
99- <text x="1300" y="544" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent.</text>
96+ <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Context gauge</text>
97+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">usage_update: warns before the</text>
98+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">conversation outgrows the agent's</text>
99+ <text x="1300" y="544" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">memory.</text>
100100 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">2 · Conversation multi-agents</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">2 · Multi-agent conversation</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Each agent has its colour; tools are folded; you can always stop</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -30,31 +30,31 @@
30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
32 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>32 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
33- <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>33+ <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
34 <text x="1244" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob · 🧑‍🚀 Riker</text>34 <text x="1244" y="144" font-size="13" fill="#6B6F7A" text-anchor="end" xml:space="preserve">👩 Alice · 🧔 Bob · 🧑‍🚀 Riker</text>
35- <text x="275" y="196" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">— Riker a rejoint le channel —</text>35+ <text x="275" y="196" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">— Riker joined the channel —</text>
36- <text x="275" y="228" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>36+ <text x="275" y="228" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
37- <text x="275" y="250.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice propose un nom pour un script qui sauvegarde mes photos</text>37+ <text x="275" y="250.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@alice suggest a name for a script that backs up my photos</text>
38 <text x="275" y="284" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>38 <text x="275" y="284" font-size="13.5" fill="#C2185B" text-anchor="start" xml:space="preserve" font-weight="700">👩 Alice</text>
39- <text x="275" y="306.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Je propose « photo-backup.sh » : court, explicite, et l'extension dit que</text>39+ <text x="275" y="306.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">I suggest "photo-backup.sh": short, clear, and the extension says</text>
40- <text x="275" y="325.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">c'est un script shell.</text>40+ <text x="275" y="325.5" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">it is a shell script.</text>
41- <text x="275" y="360" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>41+ <text x="275" y="360" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
42- <text x="275" y="382.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@bob tu valides ? @riker crée le fichier vide</text>42+ <text x="275" y="382.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@bob do you agree? @riker create the empty file</text>
43 <text x="275" y="416" font-size="13.5" fill="#00838F" text-anchor="start" xml:space="preserve" font-weight="700">🧔 Bob</text>43 <text x="275" y="416" font-size="13.5" fill="#00838F" text-anchor="start" xml:space="preserve" font-weight="700">🧔 Bob</text>
44- <text x="275" y="438.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Bon choix. Alice a raison : un nom en minuscules, sans espace.</text>44+ <text x="275" y="438.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">Good choice. Alice is right: a lowercase name, no spaces.</text>
45 <text x="275" y="472" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>45 <text x="275" y="472" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>
46- <text x="365" y="472" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">travaille…</text>46+ <text x="365" y="472" font-size="12.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-style="italic">working…</text>
47 <rect x="275" y="484" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>47 <rect x="275" y="484" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
48- <text x="289" y="509" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé</text>48+ <text x="289" y="509" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done</text>
49 <rect x="275" y="532" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>49 <rect x="275" y="532" width="560" height="40" rx="8" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
50- <text x="289" y="557" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 bash : ls -la … en cours</text>50+ <text x="289" y="557" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">▸ 🛠 bash: ls -la … running</text>
51 <rect x="271" y="754" width="849" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>51 <rect x="271" y="754" width="849" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
52 <text x="285" y="780" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve"></text>52 <text x="285" y="780" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve"></text>
53- <text x="285" y="804" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Entrée pour envoyer · Maj+Entrée nouvelle ligne</text>53+ <text x="285" y="804" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Enter to send · Shift+Enter for a new line</text>
54 <rect x="1132" y="770" width="108" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>54 <rect x="1132" y="770" width="108" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
55- <text x="1186.0" y="791.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Arrêter</text>55+ <text x="1186.0" y="791.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Stop</text>
56 <rect x="1040" y="176" width="200" height="30" rx="15" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>56 <rect x="1040" y="176" width="200" height="30" rx="15" fill="#F0F1F3" stroke="#E2E3E6" stroke-width="1"/>
57- <text x="1140" y="196" font-size="11.5" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">contexte Riker : 41 %</text>57+ <text x="1140" y="196" font-size="11.5" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Riker's context: 41%</text>
58 <circle cx="575" cy="191" r="12" fill="#6A1B9A"/>58 <circle cx="575" cy="191" r="12" fill="#6A1B9A"/>
59 <text x="575" y="195.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>59 <text x="575" y="195.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
60 <circle cx="355" cy="274" r="12" fill="#6A1B9A"/>60 <circle cx="355" cy="274" r="12" fill="#6A1B9A"/>
@@ -68,33 +68,33 @@
68 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>68 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
69 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>69 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
70 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>70 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
71- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Arrivée visible</text>71+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">A visible arrival</text>
72- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">La 1re mention invite l'agent : un message</text>72+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The first mention invites the agent: a system</text>
73- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">système le dit. On sait qui « écoute ».</text>73+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">message says so. You know who is listening.</text>
74 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>74 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
75 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>75 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
76 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>76 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
77- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Une couleur par agent</text>77+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">One colour per agent</text>
78- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Avatar + nom colorés, réponses en Markdown</text>78+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Coloured avatar and name, answers in</text>
79- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">avec code coloré (ADR 0002).</text>79+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Markdown with highlighted code (ADR 0002).</text>
80 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>80 <rect x="1284" y="260" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
81 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>81 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
82 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>82 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
83- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Outils repliés</text>83+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Folded tools</text>
84- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Une ligne par tool call : titre lisible et</text>84+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">One line per tool call: a readable title</text>
85- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">statut. Déplier montre la commande, le</text>85+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">and its status. Unfolding shows the</text>
86- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">diff ou la sortie.</text>86+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">command, the diff or the output.</text>
87 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>87 <rect x="1284" y="368" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
88 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>88 <circle cx="1306" cy="390" r="12" fill="#6A1B9A"/>
89 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>89 <text x="1306" y="394.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
90- <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Toujours un bouton Arrêter</text>90+ <text x="1328" y="395" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Always a Stop button</text>
91- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Envoie session/cancel aux agents qui</text>91+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Sends session/cancel to the agents</text>
92- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">répondent dans ce channel (ADR 0001).</text>92+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">answering in this channel (ADR 0001).</text>
93 <rect x="1284" y="458" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>93 <rect x="1284" y="458" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
94 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>94 <circle cx="1306" cy="480" r="12" fill="#6A1B9A"/>
95 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>95 <text x="1306" y="484.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
96- <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Jauge de contexte</text>96+ <text x="1328" y="485" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Context gauge</text>
97- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">usage_update : prévient avant que la</text>97+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">usage_update: warns before the</text>
98- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">conversation ne dépasse la mémoire de</text>98+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">conversation outgrows the agent's</text>
99- <text x="1300" y="544" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent.</text>99+ <text x="1300" y="544" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">memory.</text>
100 </svg>100 </svg>
modified docs/design/03-mentions-commands.png +0 -0
Binary files a/docs/design/03-mentions-commands.png and b/docs/design/03-mentions-commands.png differ
Binary files a/docs/design/03-mentions-commands.png and b/docs/design/03-mentions-commands.png differBinary files a/docs/design/03-mentions-commands.png and b/docs/design/03-mentions-commands.png differ
modified docs/design/03-mentions-commands.svg +34 -34
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">3 · Invoquer un agent (@) et une commande (/)</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Tab choisit la suggestion ; continuer à écrire filtre la liste</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">3 · Calling an agent (@) and a command (/)</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Tab picks the suggestion; typing on filters the list</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
1616 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1717 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
1818 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
1919 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>
20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
2222 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
2323 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
2424 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -29,36 +29,36 @@
2929 <text x="54" y="366" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🧑‍🚀 Riker</text>
3030 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
3131 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
32- <text x="271" y="150" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Mention d'un agent</text>
32+ <text x="271" y="150" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Mentioning an agent</text>
3333 <g filter="url(#shadow)"><rect x="271" y="164" width="420" height="150" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>
3434 <rect x="277" y="172" width="408" height="40" rx="6" fill="#E8EFFD" stroke="none"/>
3535 <text x="289" y="190" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 garfield</text>
36- <text x="289" y="206" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Garfield (traducteur) — traduit FR ⇄ EN</text>
36+ <text x="289" y="206" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Garfield (translator) — translates FR ⇄ EN</text>
3737 <text x="289" y="234" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 riker</text>
38- <text x="289" y="250" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Riker (outils) — commandes et fichiers</text>
38+ <text x="289" y="250" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Riker (tools) — commands and files</text>
3939 <text x="289" y="278" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">👩 alice</text>
40- <text x="289" y="294" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Alice (assistante) — questions simples</text>
40+ <text x="289" y="294" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Alice (assistant) — simple questions</text>
4141 <rect x="271" y="324" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
4242 <text x="285" y="350" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">@ga</text>
43- <text x="285" y="374" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab : @garfield · ↑↓ naviguer · Échap fermer</text>
44- <text x="271" y="440" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Commande d'un agent (plusieurs agents dans le channel)</text>
43+ <text x="285" y="374" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab: @garfield · ↑↓ move · Esc close</text>
44+ <text x="271" y="440" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">An agent's command (several agents in the channel)</text>
4545 <g filter="url(#shadow)"><rect x="271" y="454" width="520" height="196" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>
4646 <rect x="277" y="462" width="508" height="42" rx="6" fill="#E8EFFD" stroke="none"/>
4747 <text x="289" y="481" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:idioms</text>
48- <text x="771" y="481" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>
49- <text x="289" y="498" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Expressions idiomatiques et faux amis</text>
48+ <text x="771" y="481" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
49+ <text x="289" y="498" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Idioms and false friends</text>
5050 <text x="289" y="527" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:styles</text>
51- <text x="771" y="527" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>
52- <text x="289" y="544" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Plusieurs versions d'une traduction</text>
51+ <text x="771" y="527" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
52+ <text x="289" y="544" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Several versions of a translation</text>
5353 <text x="289" y="573" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:email-letter</text>
54- <text x="771" y="573" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>
55- <text x="289" y="590" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">E-mails et lettres : formules, tu ou vous</text>
54+ <text x="771" y="573" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
55+ <text x="289" y="590" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">E-mails and letters: greetings, tu or vous</text>
5656 <text x="289" y="619" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/alice:compact</text>
5757 <text x="771" y="619" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>
58- <text x="289" y="636" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Résumer l'historique maintenant</text>
58+ <text x="289" y="636" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Summarise the history now</text>
5959 <rect x="271" y="660" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
6060 <text x="285" y="686" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">/gar</text>
61- <text x="285" y="710" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab : /garfield:idioms · le texte après la commande est son argument</text>
61+ <text x="285" y="710" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab: /garfield:idioms · the text after the command is its argument</text>
6262 <circle cx="705" cy="180" r="12" fill="#6A1B9A"/>
6363 <text x="705" y="184.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
6464 <circle cx="571" cy="356" r="12" fill="#6A1B9A"/>
@@ -72,31 +72,31 @@
7272 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7373 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
7474 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
75- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">@ liste les agents de la config</text>
76- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Nom technique + titre + description.</text>
77- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Filtrage flou sur nom et titre (« gar »).</text>
75+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">@ lists the configured agents</text>
76+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Technical name + title + description.</text>
77+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Fuzzy filter on name and title ("gar").</text>
7878 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7979 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
8080 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
81- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Tab sélectionne, sinon on écrit</text>
82- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Comportement demandé dans le draft (#1).</text>
83- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Échap ferme sans rien insérer.</text>
81+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Tab picks, otherwise you type</text>
82+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Behaviour asked for in the draft (#1).</text>
83+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Esc closes without inserting anything.</text>
8484 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8585 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
8686 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
87- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">/agent:commande en multi-agents</text>
88- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Avec un seul agent : /commande. Sidekicks</text>
89- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">envoie « /idioms … » au seul Garfield.</text>
87+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">/agent:command with several agents</text>
88+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">With one agent: /command. Sidekicks</text>
89+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">sends "/idioms …" to Garfield only.</text>
9090 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9191 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
9292 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
93- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Skills et commandes intégrées</text>
94- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">mm annonce compact, new, unload et ses</text>
95- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">skills (available_commands_update).</text>
93+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Skills and built-in commands</text>
94+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">mm announces compact, new, unload and</text>
95+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">its skills (available_commands_update).</text>
9696 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9797 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>
9898 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
99- <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Indice d'argument</text>
100- <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">input.hint de la commande ACP, affiché</text>
101- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">tant que l'argument n'est pas saisi.</text>
99+ <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Argument hint</text>
100+ <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The ACP command's input.hint, shown</text>
101+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">until the argument is typed.</text>
102102 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">3 · Invoquer un agent (@) et une commande (/)</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">3 · Calling an agent (@) and a command (/)</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Tab choisit la suggestion ; continuer à écrire filtre la liste</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Tab picks the suggestion; typing on filters the list</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,14 +11,14 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>19 <rect x="28" y="158" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
20- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># général</text>20+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># general</text>
21- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>21+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>24 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -29,36 +29,36 @@
29 <text x="54" y="366" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🧑‍🚀 Riker</text>29 <text x="54" y="366" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🧑‍🚀 Riker</text>
30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>30 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
32- <text x="271" y="150" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Mention d'un agent</text>32+ <text x="271" y="150" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Mentioning an agent</text>
33 <g filter="url(#shadow)"><rect x="271" y="164" width="420" height="150" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>33 <g filter="url(#shadow)"><rect x="271" y="164" width="420" height="150" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>
34 <rect x="277" y="172" width="408" height="40" rx="6" fill="#E8EFFD" stroke="none"/>34 <rect x="277" y="172" width="408" height="40" rx="6" fill="#E8EFFD" stroke="none"/>
35 <text x="289" y="190" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 garfield</text>35 <text x="289" y="190" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 garfield</text>
36- <text x="289" y="206" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Garfield (traducteur) — traduit FR ⇄ EN</text>36+ <text x="289" y="206" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Garfield (translator) — translates FR ⇄ EN</text>
37 <text x="289" y="234" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 riker</text>37 <text x="289" y="234" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 riker</text>
38- <text x="289" y="250" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Riker (outils) — commandes et fichiers</text>38+ <text x="289" y="250" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Riker (tools) — commands and files</text>
39 <text x="289" y="278" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">👩 alice</text>39 <text x="289" y="278" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">👩 alice</text>
40- <text x="289" y="294" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Alice (assistante) — questions simples</text>40+ <text x="289" y="294" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Alice (assistant) — simple questions</text>
41 <rect x="271" y="324" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>41 <rect x="271" y="324" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
42 <text x="285" y="350" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">@ga</text>42 <text x="285" y="350" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">@ga</text>
43- <text x="285" y="374" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab : @garfield · ↑↓ naviguer · Échap fermer</text>43+ <text x="285" y="374" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab: @garfield · ↑↓ move · Esc close</text>
44- <text x="271" y="440" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">Commande d'un agent (plusieurs agents dans le channel)</text>44+ <text x="271" y="440" font-size="14" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">An agent's command (several agents in the channel)</text>
45 <g filter="url(#shadow)"><rect x="271" y="454" width="520" height="196" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>45 <g filter="url(#shadow)"><rect x="271" y="454" width="520" height="196" rx="10" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/></g>
46 <rect x="277" y="462" width="508" height="42" rx="6" fill="#E8EFFD" stroke="none"/>46 <rect x="277" y="462" width="508" height="42" rx="6" fill="#E8EFFD" stroke="none"/>
47 <text x="289" y="481" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:idioms</text>47 <text x="289" y="481" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:idioms</text>
48- <text x="771" y="481" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>48+ <text x="771" y="481" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
49- <text x="289" y="498" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Expressions idiomatiques et faux amis</text>49+ <text x="289" y="498" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Idioms and false friends</text>
50 <text x="289" y="527" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:styles</text>50 <text x="289" y="527" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:styles</text>
51- <text x="771" y="527" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>51+ <text x="771" y="527" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
52- <text x="289" y="544" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Plusieurs versions d'une traduction</text>52+ <text x="289" y="544" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Several versions of a translation</text>
53 <text x="289" y="573" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:email-letter</text>53 <text x="289" y="573" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/garfield:email-letter</text>
54- <text x="771" y="573" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[texte]</text>54+ <text x="771" y="573" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">[text]</text>
55- <text x="289" y="590" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">E-mails et lettres : formules, tu ou vous</text>55+ <text x="289" y="590" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">E-mails and letters: greetings, tu or vous</text>
56 <text x="289" y="619" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/alice:compact</text>56 <text x="289" y="619" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700" font-family="DejaVu Sans Mono, monospace">/alice:compact</text>
57 <text x="771" y="619" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>57 <text x="771" y="619" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>
58- <text x="289" y="636" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Résumer l'historique maintenant</text>58+ <text x="289" y="636" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Summarise the history now</text>
59 <rect x="271" y="660" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>59 <rect x="271" y="660" width="969" height="64" rx="10" fill="#FFFFFF" stroke="#3B6FE0" stroke-width="1.5"/>
60 <text x="285" y="686" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">/gar</text>60 <text x="285" y="686" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve">/gar</text>
61- <text x="285" y="710" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab : /garfield:idioms · le texte après la commande est son argument</text>61+ <text x="285" y="710" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Tab: /garfield:idioms · the text after the command is its argument</text>
62 <circle cx="705" cy="180" r="12" fill="#6A1B9A"/>62 <circle cx="705" cy="180" r="12" fill="#6A1B9A"/>
63 <text x="705" y="184.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>63 <text x="705" y="184.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
64 <circle cx="571" cy="356" r="12" fill="#6A1B9A"/>64 <circle cx="571" cy="356" r="12" fill="#6A1B9A"/>
@@ -72,31 +72,31 @@
72 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>72 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
73 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>73 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
74 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>74 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
75- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">@ liste les agents de la config</text>75+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">@ lists the configured agents</text>
76- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Nom technique + titre + description.</text>76+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Technical name + title + description.</text>
77- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Filtrage flou sur nom et titre (« gar »).</text>77+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Fuzzy filter on name and title ("gar").</text>
78 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>78 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
79 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>79 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
80 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>80 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
81- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Tab sélectionne, sinon on écrit</text>81+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Tab picks, otherwise you type</text>
82- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Comportement demandé dans le draft (#1).</text>82+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Behaviour asked for in the draft (#1).</text>
83- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Échap ferme sans rien insérer.</text>83+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Esc closes without inserting anything.</text>
84 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>84 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
85 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>85 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
86 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>86 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
87- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">/agent:commande en multi-agents</text>87+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">/agent:command with several agents</text>
88- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Avec un seul agent : /commande. Sidekicks</text>88+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">With one agent: /command. Sidekicks</text>
89- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">envoie « /idioms … » au seul Garfield.</text>89+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">sends "/idioms …" to Garfield only.</text>
90 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>90 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
91 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>91 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
92 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>92 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
93- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Skills et commandes intégrées</text>93+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Skills and built-in commands</text>
94- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">mm annonce compact, new, unload et ses</text>94+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">mm announces compact, new, unload and</text>
95- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">skills (available_commands_update).</text>95+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">its skills (available_commands_update).</text>
96 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>96 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
97 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>97 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>
98 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>98 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
99- <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Indice d'argument</text>99+ <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Argument hint</text>
100- <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">input.hint de la commande ACP, affiché</text>100+ <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The ACP command's input.hint, shown</text>
101- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">tant que l'argument n'est pas saisi.</text>101+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">until the argument is typed.</text>
102 </svg>102 </svg>
modified docs/design/04-permission.png +0 -0
Binary files a/docs/design/04-permission.png and b/docs/design/04-permission.png differ
Binary files a/docs/design/04-permission.png and b/docs/design/04-permission.png differBinary files a/docs/design/04-permission.png and b/docs/design/04-permission.png differ
modified docs/design/04-permission.svg +33 -33
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">4 · Demande de permission</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">En langage clair, sur la conversation concernée, avec une sortie de secours</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">4 · Permission request</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">In plain words, on the conversation concerned, with a way out</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,13 +11,13 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
1616 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1717 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
1818 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># général</text>
20- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>
19+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># general</text>
20+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
2121 <rect x="28" y="218" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
2222 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>
2323 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
@@ -31,28 +31,28 @@
3131 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
3232 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
3333 <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>
34- <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>
35- <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@riker supprime les fichiers temporaires du dossier</text>
34+ <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
35+ <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@riker delete the temporary files in the folder</text>
3636 <text x="275" y="252" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>
37- <text x="365" y="252" font-size="12.5" fill="#C98A1B" text-anchor="start" xml:space="preserve" font-style="italic">attend votre réponse</text>
37+ <text x="365" y="252" font-size="12.5" fill="#C98A1B" text-anchor="start" xml:space="preserve" font-style="italic">waiting for your answer</text>
3838 <g filter="url(#shadow)"><rect x="275" y="270" width="720" height="330" rx="12" fill="#FFFFFF" stroke="#C98A1B" stroke-width="2"/></g>
39- <text x="295" y="304" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur</text>
39+ <text x="295" y="304" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker wants to run a command on your computer</text>
4040 <rect x="295" y="322" width="680" height="40" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
4141 <text x="309" y="347" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">rm -f *.tmp</text>
42- <text x="295" y="388" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp</text>
43- <text x="295" y="408" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">dans agents/riker . Ils ne passent pas par la corbeille.</text>
44- <text x="295" y="438" font-size="13" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">⚠ Action irréversible</text>
42+ <text x="295" y="388" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">What it does: permanently deletes the files ending in .tmp</text>
43+ <text x="295" y="408" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">in agents/riker . They do not go to the trash.</text>
44+ <text x="295" y="438" font-size="13" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">⚠ Cannot be undone</text>
4545 <rect x="295" y="460" width="170" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
46- <text x="380.0" y="481.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Autoriser une fois</text>
46+ <text x="380.0" y="481.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Allow once</text>
4747 <rect x="475" y="460" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
48- <text x="625.0" y="481.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Toujours autoriser bash ici</text>
48+ <text x="625.0" y="481.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Always allow bash here</text>
4949 <rect x="295" y="504" width="170" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
50- <text x="380.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Refuser</text>
50+ <text x="380.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Reject</text>
5151 <rect x="475" y="504" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
52- <text x="625.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Ne plus jamais proposer bash ici</text>
52+ <text x="625.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Never offer bash here again</text>
5353 <rect x="795" y="504" width="180" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
54- <text x="885.0" y="525.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Arrêter Riker</text>
55- <text x="295" y="570" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Détails techniques (tool call bash, rawInput)</text>
54+ <text x="885.0" y="525.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Stop Riker</text>
55+ <text x="295" y="570" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Technical details (bash tool call, rawInput)</text>
5656 <circle cx="979" cy="300" r="12" fill="#6A1B9A"/>
5757 <text x="979" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
5858 <circle cx="955" cy="398" r="12" fill="#6A1B9A"/>
@@ -66,32 +66,32 @@
6666 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
6767 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
6868 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
69- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Attaché à la conversation</text>
70- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Bloque seulement la session de Riker ; les</text>
71- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">autres channels continuent (#17).</text>
69+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Attached to the conversation</text>
70+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Blocks only Riker's session; the other</text>
71+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">channels go on (#17).</text>
7272 <rect x="1284" y="170" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
7373 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
7474 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
75- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Expliquer, pas montrer du JSON</text>
76- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Titre ACP + kind + rawInput traduits en</text>
77- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">phrase. Les actions destructives sont</text>
78- <text x="1300" y="256" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">signalées.</text>
75+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Explain, do not show JSON</text>
76+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">ACP title + kind + rawInput turned into</text>
77+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">a sentence. Destructive actions are</text>
78+ <text x="1300" y="256" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">flagged.</text>
7979 <rect x="1284" y="278" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8080 <circle cx="1306" cy="300" r="12" fill="#6A1B9A"/>
8181 <text x="1306" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
82- <text x="1328" y="305" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Les 4 options ACP, par ids</text>
82+ <text x="1328" y="305" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">The 4 ACP options, by id</text>
8383 <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">allow · allow_always · reject · never.</text>
84- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">« Ne plus jamais » est mis en avant au</text>
85- <text x="1300" y="364" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">lieu de « Refuser une fois ».</text>
84+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">"Never again" is put forward rather</text>
85+ <text x="1300" y="364" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">than "Reject once".</text>
8686 <rect x="1284" y="386" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8787 <circle cx="1306" cy="408" r="12" fill="#6A1B9A"/>
8888 <text x="1306" y="412.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
89- <text x="1328" y="413" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Toujours une sortie de secours</text>
90- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Spike ACP : après un refus, un petit modèle</text>
91- <text x="1300" y="454" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">a réessayé 40 fois. Arrêter = session/cancel.</text>
89+ <text x="1328" y="413" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Always a way out</text>
90+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">ACP spike: after a rejection, a small model</text>
91+ <text x="1300" y="454" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">retried 40 times. Stop = session/cancel.</text>
9292 <rect x="1284" y="476" width="356" height="58" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9393 <circle cx="1306" cy="498" r="12" fill="#6A1B9A"/>
9494 <text x="1306" y="502.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
95- <text x="1328" y="503" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Détails pour les curieux</text>
96- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Repliés par défaut.</text>
95+ <text x="1328" y="503" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Details for the curious</text>
96+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Folded by default.</text>
9797 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">4 · Demande de permission</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">4 · Permission request</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">En langage clair, sur la conversation concernée, avec une sortie de secours</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">In plain words, on the conversation concerned, with a way out</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,13 +11,13 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># général</text>19+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># general</text>
20- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>20+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
21 <rect x="28" y="218" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>21 <rect x="28" y="218" width="214" height="26" rx="6" fill="#F0F1F3" stroke="none"/>
22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>22 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>
23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>23 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
@@ -31,28 +31,28 @@
31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>31 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
32 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>32 <rect x="251" y="116" width="1009" height="44" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
33 <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>33 <text x="267" y="144" font-size="15" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700"># code</text>
34- <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Vous</text>34+ <text x="275" y="196" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">You</text>
35- <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@riker supprime les fichiers temporaires du dossier</text>35+ <text x="275" y="218.0" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">@riker delete the temporary files in the folder</text>
36 <text x="275" y="252" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>36 <text x="275" y="252" font-size="13.5" fill="#5E35B1" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker</text>
37- <text x="365" y="252" font-size="12.5" fill="#C98A1B" text-anchor="start" xml:space="preserve" font-style="italic">attend votre réponse</text>37+ <text x="365" y="252" font-size="12.5" fill="#C98A1B" text-anchor="start" xml:space="preserve" font-style="italic">waiting for your answer</text>
38 <g filter="url(#shadow)"><rect x="275" y="270" width="720" height="330" rx="12" fill="#FFFFFF" stroke="#C98A1B" stroke-width="2"/></g>38 <g filter="url(#shadow)"><rect x="275" y="270" width="720" height="330" rx="12" fill="#FFFFFF" stroke="#C98A1B" stroke-width="2"/></g>
39- <text x="295" y="304" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur</text>39+ <text x="295" y="304" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🧑‍🚀 Riker wants to run a command on your computer</text>
40 <rect x="295" y="322" width="680" height="40" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>40 <rect x="295" y="322" width="680" height="40" rx="6" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
41 <text x="309" y="347" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">rm -f *.tmp</text>41 <text x="309" y="347" font-size="14" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">rm -f *.tmp</text>
42- <text x="295" y="388" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp</text>42+ <text x="295" y="388" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">What it does: permanently deletes the files ending in .tmp</text>
43- <text x="295" y="408" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">dans agents/riker . Ils ne passent pas par la corbeille.</text>43+ <text x="295" y="408" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">in agents/riker . They do not go to the trash.</text>
44- <text x="295" y="438" font-size="13" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">⚠ Action irréversible</text>44+ <text x="295" y="438" font-size="13" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">⚠ Cannot be undone</text>
45 <rect x="295" y="460" width="170" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>45 <rect x="295" y="460" width="170" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
46- <text x="380.0" y="481.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Autoriser une fois</text>46+ <text x="380.0" y="481.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Allow once</text>
47 <rect x="475" y="460" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>47 <rect x="475" y="460" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
48- <text x="625.0" y="481.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Toujours autoriser bash ici</text>48+ <text x="625.0" y="481.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Always allow bash here</text>
49 <rect x="295" y="504" width="170" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>49 <rect x="295" y="504" width="170" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
50- <text x="380.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Refuser</text>50+ <text x="380.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Reject</text>
51 <rect x="475" y="504" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>51 <rect x="475" y="504" width="300" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
52- <text x="625.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Ne plus jamais proposer bash ici</text>52+ <text x="625.0" y="525.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Never offer bash here again</text>
53 <rect x="795" y="504" width="180" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>53 <rect x="795" y="504" width="180" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
54- <text x="885.0" y="525.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Arrêter Riker</text>54+ <text x="885.0" y="525.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">■ Stop Riker</text>
55- <text x="295" y="570" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Détails techniques (tool call bash, rawInput)</text>55+ <text x="295" y="570" font-size="12" fill="#6B6F7A" text-anchor="start" xml:space="preserve">▸ Technical details (bash tool call, rawInput)</text>
56 <circle cx="979" cy="300" r="12" fill="#6A1B9A"/>56 <circle cx="979" cy="300" r="12" fill="#6A1B9A"/>
57 <text x="979" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>57 <text x="979" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
58 <circle cx="955" cy="398" r="12" fill="#6A1B9A"/>58 <circle cx="955" cy="398" r="12" fill="#6A1B9A"/>
@@ -66,32 +66,32 @@
66 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>66 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
67 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>67 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
68 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>68 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
69- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Attaché à la conversation</text>69+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Attached to the conversation</text>
70- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Bloque seulement la session de Riker ; les</text>70+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Blocks only Riker's session; the other</text>
71- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">autres channels continuent (#17).</text>71+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">channels go on (#17).</text>
72 <rect x="1284" y="170" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>72 <rect x="1284" y="170" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
73 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>73 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
74 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>74 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
75- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Expliquer, pas montrer du JSON</text>75+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Explain, do not show JSON</text>
76- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Titre ACP + kind + rawInput traduits en</text>76+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">ACP title + kind + rawInput turned into</text>
77- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">phrase. Les actions destructives sont</text>77+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">a sentence. Destructive actions are</text>
78- <text x="1300" y="256" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">signalées.</text>78+ <text x="1300" y="256" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">flagged.</text>
79 <rect x="1284" y="278" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>79 <rect x="1284" y="278" width="356" height="94" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
80 <circle cx="1306" cy="300" r="12" fill="#6A1B9A"/>80 <circle cx="1306" cy="300" r="12" fill="#6A1B9A"/>
81 <text x="1306" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>81 <text x="1306" y="304.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
82- <text x="1328" y="305" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Les 4 options ACP, par ids</text>82+ <text x="1328" y="305" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">The 4 ACP options, by id</text>
83 <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">allow · allow_always · reject · never.</text>83 <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">allow · allow_always · reject · never.</text>
84- <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">« Ne plus jamais » est mis en avant au</text>84+ <text x="1300" y="346" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">"Never again" is put forward rather</text>
85- <text x="1300" y="364" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">lieu de « Refuser une fois ».</text>85+ <text x="1300" y="364" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">than "Reject once".</text>
86 <rect x="1284" y="386" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>86 <rect x="1284" y="386" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
87 <circle cx="1306" cy="408" r="12" fill="#6A1B9A"/>87 <circle cx="1306" cy="408" r="12" fill="#6A1B9A"/>
88 <text x="1306" y="412.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>88 <text x="1306" y="412.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
89- <text x="1328" y="413" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Toujours une sortie de secours</text>89+ <text x="1328" y="413" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Always a way out</text>
90- <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Spike ACP : après un refus, un petit modèle</text>90+ <text x="1300" y="436" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">ACP spike: after a rejection, a small model</text>
91- <text x="1300" y="454" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">a réessayé 40 fois. Arrêter = session/cancel.</text>91+ <text x="1300" y="454" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">retried 40 times. Stop = session/cancel.</text>
92 <rect x="1284" y="476" width="356" height="58" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>92 <rect x="1284" y="476" width="356" height="58" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
93 <circle cx="1306" cy="498" r="12" fill="#6A1B9A"/>93 <circle cx="1306" cy="498" r="12" fill="#6A1B9A"/>
94 <text x="1306" y="502.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>94 <text x="1306" y="502.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
95- <text x="1328" y="503" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Détails pour les curieux</text>95+ <text x="1328" y="503" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Details for the curious</text>
96- <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Repliés par défaut.</text>96+ <text x="1300" y="526" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Folded by default.</text>
97 </svg>97 </svg>
modified docs/design/05-agents-panel.png +0 -0
Binary files a/docs/design/05-agents-panel.png and b/docs/design/05-agents-panel.png differ
Binary files a/docs/design/05-agents-panel.png and b/docs/design/05-agents-panel.png differBinary files a/docs/design/05-agents-panel.png and b/docs/design/05-agents-panel.png differ
modified docs/design/05-agents-panel.svg +35 -35
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">5 · Panel Agents : fichiers, éditeur, terminal</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">5 · Agents panel: files, editor, terminal</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">The agent's workspace, whether it runs locally, in a container or in an sbx sandbox</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,13 +11,13 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
1616 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1717 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
1818 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># général</text>
20- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>
19+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># general</text>
20+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
2121 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
2222 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
2323 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -29,15 +29,15 @@
2929 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
3030 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
3131 <rect x="251" y="116" width="1009" height="56" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
32- <text x="267" y="142" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 Garfield (traducteur)</text>
33- <text x="267" y="162" font-size="12" fill="#C98A1B" text-anchor="start" xml:space="preserve">● occupé · local · agents/garfield · gemma-4-E4B</text>
32+ <text x="267" y="142" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 Garfield (translator)</text>
33+ <text x="267" y="162" font-size="12" fill="#C98A1B" text-anchor="start" xml:space="preserve">● busy · local · agents/garfield · gemma-4-E4B</text>
3434 <rect x="1010" y="128" width="110" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
35- <text x="1065.0" y="149.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Redémarrer</text>
35+ <text x="1065.0" y="149.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Restart</text>
3636 <rect x="1130" y="128" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
37- <text x="1185.0" y="149.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Arrêter</text>
38- <text x="275" y="198" font-size="13.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve" font-weight="700">Fichiers</text>
37+ <text x="1185.0" y="149.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Stop</text>
38+ <text x="275" y="198" font-size="13.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve" font-weight="700">Files</text>
3939 <text x="375" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Terminal</text>
40- <text x="475" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Journal</text>
40+ <text x="475" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Log</text>
4141 <rect x="267" y="206" width="70" height="2" rx="0" fill="#3B6FE0" stroke="none"/>
4242 <rect x="251" y="216" width="260" height="420" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
4343 <text x="265" y="245" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">▾ garfield</text>
@@ -50,20 +50,20 @@
5050 <text x="265" y="401" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ idioms</text>
5151 <text x="265" y="427" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ styles</text>
5252 <text x="265" y="453" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ technical-text</text>
53- <text x="265" y="479" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> traduction.md</text>
54- <text x="265" y="516" font-size="12.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve">+ Nouveau fichier</text>
53+ <text x="265" y="479" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> translation.md</text>
54+ <text x="265" y="516" font-size="12.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve">+ New file</text>
5555 <rect x="521" y="216" width="729" height="420" rx="0" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
5656 <text x="535" y="240" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS.md</text>
57- <text x="535" y="270" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"># Style de traduction</text>
57+ <text x="535" y="270" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"># Translation style</text>
5858 <text x="535" y="292" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>
59- <text x="535" y="314" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Registre : celui du texte source.</text>
60- <text x="535" y="336" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Typographie française : espaces insécables</text>
61- <text x="535" y="358" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> avant ; : ! ? et entre « guillemets ».</text>
62- <text x="535" y="380" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Ne jamais traduire le code ni les noms propres.</text>
59+ <text x="535" y="314" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Register: the one of the source text.</text>
60+ <text x="535" y="336" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- French typography: non-breaking spaces</text>
61+ <text x="535" y="358" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> before ; : ! ? and inside « quotes ».</text>
62+ <text x="535" y="380" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Never translate code or proper names.</text>
6363 <rect x="1010" y="590" width="110" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
64- <text x="1065.0" y="611.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Enregistrer</text>
64+ <text x="1065.0" y="611.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Save</text>
6565 <rect x="1130" y="590" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
66- <text x="1185.0" y="611.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Supprimer</text>
66+ <text x="1185.0" y="611.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Delete</text>
6767 <rect x="251" y="646" width="1009" height="188" rx="0" fill="#111317" stroke="#E2E3E6" stroke-width="1"/>
6868 <text x="265" y="672" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">garfield $ ls skills</text>
6969 <text x="265" y="692" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">email-letter idioms styles technical-text</text>
@@ -81,31 +81,31 @@
8181 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8282 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
8383 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
84- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">État et emplacement de l'agent</text>
85- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Statut temps réel (#11), runtime (local,</text>
86- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">container, sbx), dossier et modèle.</text>
84+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">The agent's state and location</text>
85+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Live status (#11), runtime (local,</text>
86+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">container, sbx), folder and model.</text>
8787 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
8888 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
8989 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
90- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Arbre du workspace</text>
91- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Chargement paresseux, rafraîchi quand</text>
92- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent modifie un fichier (#29).</text>
90+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Workspace tree</text>
91+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Loaded lazily, refreshed when the</text>
92+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">agent changes a file (#29).</text>
9393 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
9494 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
9595 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
96- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Éditer, supprimer avec confirmation</text>
97- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Même éditeur que Settings. Conflit signalé</text>
98- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">si l'agent modifie le fichier (#30).</text>
96+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Edit, delete with confirmation</text>
97+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Same editor as Settings. A conflict is</text>
98+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">flagged if the agent changes the file (#30).</text>
9999 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
100100 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
101101 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
102- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Terminal sur le workspace</text>
103- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">PTY local, docker exec ou sbx selon le</text>
104- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">runtime (#28, #31). Replié par défaut.</text>
102+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Terminal in the workspace</text>
103+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Local PTY, docker exec or sbx depending on</text>
104+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">the runtime (#28, #31). Folded by default.</text>
105105 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
106106 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>
107107 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
108- <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Journal</text>
109- <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">stderr de l'agent, pour comprendre</text>
110- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">une erreur sans ouvrir un terminal.</text>
108+ <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Log</text>
109+ <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The agent's stderr, to understand</text>
110+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">an error without opening a terminal.</text>
111111 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">5 · Panel Agents : fichiers, éditeur, terminal</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">5 · Agents panel: files, editor, terminal</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">The agent's workspace, whether it runs locally, in a container or in an sbx sandbox</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,13 +11,13 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">● 2 of 2 agents running · config: ~/agents/team.yaml</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>16 <rect x="20" y="116" width="230" height="718" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>17 <text x="36" y="144" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">CHANNELS</text>
18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>18 <text x="234" y="144" font-size="16" fill="#6B6F7A" text-anchor="end" xml:space="preserve" font-weight="700">+</text>
19- <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># général</text>19+ <text x="38" y="176" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># general</text>
20- <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># traductions</text>20+ <text x="38" y="206" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># translations</text>
21 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>21 <text x="38" y="236" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve"># code</text>
22 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>22 <text x="36" y="274" font-size="11" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS</text>
23 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>23 <circle cx="42" cy="301" r="4.5" fill="#3E9B4F"/>
@@ -29,15 +29,15 @@
29 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>29 <circle cx="42" cy="391" r="4.5" fill="#C98A1B"/>
30 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>30 <text x="54" y="396" font-size="13.5" fill="#1D1F24" text-anchor="start" xml:space="preserve">🐮 Garfield</text>
31 <rect x="251" y="116" width="1009" height="56" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>31 <rect x="251" y="116" width="1009" height="56" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
32- <text x="267" y="142" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 Garfield (traducteur)</text>32+ <text x="267" y="142" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">🐮 Garfield (translator)</text>
33- <text x="267" y="162" font-size="12" fill="#C98A1B" text-anchor="start" xml:space="preserve">● occupé · local · agents/garfield · gemma-4-E4B</text>33+ <text x="267" y="162" font-size="12" fill="#C98A1B" text-anchor="start" xml:space="preserve">● busy · local · agents/garfield · gemma-4-E4B</text>
34 <rect x="1010" y="128" width="110" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>34 <rect x="1010" y="128" width="110" height="32" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
35- <text x="1065.0" y="149.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Redémarrer</text>35+ <text x="1065.0" y="149.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Restart</text>
36 <rect x="1130" y="128" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>36 <rect x="1130" y="128" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
37- <text x="1185.0" y="149.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Arrêter</text>37+ <text x="1185.0" y="149.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Stop</text>
38- <text x="275" y="198" font-size="13.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve" font-weight="700">Fichiers</text>38+ <text x="275" y="198" font-size="13.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve" font-weight="700">Files</text>
39 <text x="375" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Terminal</text>39 <text x="375" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Terminal</text>
40- <text x="475" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Journal</text>40+ <text x="475" y="198" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Log</text>
41 <rect x="267" y="206" width="70" height="2" rx="0" fill="#3B6FE0" stroke="none"/>41 <rect x="267" y="206" width="70" height="2" rx="0" fill="#3B6FE0" stroke="none"/>
42 <rect x="251" y="216" width="260" height="420" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>42 <rect x="251" y="216" width="260" height="420" rx="0" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
43 <text x="265" y="245" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">▾ garfield</text>43 <text x="265" y="245" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">▾ garfield</text>
@@ -50,20 +50,20 @@
50 <text x="265" y="401" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ idioms</text>50 <text x="265" y="401" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ idioms</text>
51 <text x="265" y="427" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ styles</text>51 <text x="265" y="427" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ styles</text>
52 <text x="265" y="453" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ technical-text</text>52 <text x="265" y="453" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> ▸ technical-text</text>
53- <text x="265" y="479" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> traduction.md</text>53+ <text x="265" y="479" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> translation.md</text>
54- <text x="265" y="516" font-size="12.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve">+ Nouveau fichier</text>54+ <text x="265" y="516" font-size="12.5" fill="#3B6FE0" text-anchor="start" xml:space="preserve">+ New file</text>
55 <rect x="521" y="216" width="729" height="420" rx="0" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>55 <rect x="521" y="216" width="729" height="420" rx="0" fill="#F4F5F7" stroke="#E2E3E6" stroke-width="1"/>
56 <text x="535" y="240" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS.md</text>56 <text x="535" y="240" font-size="13" fill="#6B6F7A" text-anchor="start" xml:space="preserve" font-weight="700">AGENTS.md</text>
57- <text x="535" y="270" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"># Style de traduction</text>57+ <text x="535" y="270" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"># Translation style</text>
58 <text x="535" y="292" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>58 <text x="535" y="292" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"></text>
59- <text x="535" y="314" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Registre : celui du texte source.</text>59+ <text x="535" y="314" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Register: the one of the source text.</text>
60- <text x="535" y="336" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Typographie française : espaces insécables</text>60+ <text x="535" y="336" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- French typography: non-breaking spaces</text>
61- <text x="535" y="358" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> avant ; : ! ? et entre « guillemets ».</text>61+ <text x="535" y="358" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace"> before ; : ! ? and inside « quotes ».</text>
62- <text x="535" y="380" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Ne jamais traduire le code ni les noms propres.</text>62+ <text x="535" y="380" font-size="12.5" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">- Never translate code or proper names.</text>
63 <rect x="1010" y="590" width="110" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>63 <rect x="1010" y="590" width="110" height="32" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
64- <text x="1065.0" y="611.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Enregistrer</text>64+ <text x="1065.0" y="611.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">Save</text>
65 <rect x="1130" y="590" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>65 <rect x="1130" y="590" width="110" height="32" rx="6" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
66- <text x="1185.0" y="611.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Supprimer</text>66+ <text x="1185.0" y="611.0" font-size="13" fill="#D64561" text-anchor="middle" xml:space="preserve">Delete</text>
67 <rect x="251" y="646" width="1009" height="188" rx="0" fill="#111317" stroke="#E2E3E6" stroke-width="1"/>67 <rect x="251" y="646" width="1009" height="188" rx="0" fill="#111317" stroke="#E2E3E6" stroke-width="1"/>
68 <text x="265" y="672" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">garfield $ ls skills</text>68 <text x="265" y="672" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">garfield $ ls skills</text>
69 <text x="265" y="692" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">email-letter idioms styles technical-text</text>69 <text x="265" y="692" font-size="12.5" fill="#D7DAE0" text-anchor="start" xml:space="preserve" font-family="DejaVu Sans Mono, monospace">email-letter idioms styles technical-text</text>
@@ -81,31 +81,31 @@
81 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>81 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
82 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>82 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
83 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>83 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
84- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">État et emplacement de l'agent</text>84+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">The agent's state and location</text>
85- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Statut temps réel (#11), runtime (local,</text>85+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Live status (#11), runtime (local,</text>
86- <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">container, sbx), dossier et modèle.</text>86+ <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">container, sbx), folder and model.</text>
87 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>87 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
88 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>88 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
89 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>89 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
90- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Arbre du workspace</text>90+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Workspace tree</text>
91- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Chargement paresseux, rafraîchi quand</text>91+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Loaded lazily, refreshed when the</text>
92- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'agent modifie un fichier (#29).</text>92+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">agent changes a file (#29).</text>
93 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>93 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
94 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>94 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
95 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>95 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
96- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Éditer, supprimer avec confirmation</text>96+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Edit, delete with confirmation</text>
97- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Même éditeur que Settings. Conflit signalé</text>97+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Same editor as Settings. A conflict is</text>
98- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">si l'agent modifie le fichier (#30).</text>98+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">flagged if the agent changes the file (#30).</text>
99 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>99 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
100 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>100 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
101 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>101 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
102- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Terminal sur le workspace</text>102+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Terminal in the workspace</text>
103- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">PTY local, docker exec ou sbx selon le</text>103+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Local PTY, docker exec or sbx depending on</text>
104- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">runtime (#28, #31). Replié par défaut.</text>104+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">the runtime (#28, #31). Folded by default.</text>
105 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>105 <rect x="1284" y="440" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
106 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>106 <circle cx="1306" cy="462" r="12" fill="#6A1B9A"/>
107 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>107 <text x="1306" y="466.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">5</text>
108- <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Journal</text>108+ <text x="1328" y="467" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Log</text>
109- <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">stderr de l'agent, pour comprendre</text>109+ <text x="1300" y="490" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The agent's stderr, to understand</text>
110- <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">une erreur sans ouvrir un terminal.</text>110+ <text x="1300" y="508" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">an error without opening a terminal.</text>
111 </svg>111 </svg>
modified docs/design/06-first-launch.png +0 -0
Binary files a/docs/design/06-first-launch.png and b/docs/design/06-first-launch.png differ
Binary files a/docs/design/06-first-launch.png and b/docs/design/06-first-launch.png differBinary files a/docs/design/06-first-launch.png and b/docs/design/06-first-launch.png differ
modified docs/design/06-first-launch.svg +27 -27
@@ -1,8 +1,8 @@
11 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
33 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">6 · Premier lancement</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Moins d'une minute pour savoir quoi faire, sans lire de documentation</text>
4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">6 · First launch</text>
5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Less than a minute to know what to do, without reading any documentation</text>
66 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
77 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
88 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,33 +11,33 @@
1111 <circle cx="80" cy="98" r="6" fill="#28C840"/>
1212 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
1313 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">○ aucun agent configuré · config : créée à l'emplacement par défaut</text>
14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">○ no agent configured · config: created at the default location</text>
1515 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16- <text x="640.0" y="200" font-size="26" fill="#1D1F24" text-anchor="middle" xml:space="preserve" font-weight="700">👋 Bienvenue dans Sidekicks</text>
17- <text x="640.0" y="234" font-size="15" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Discutez avec des agents d'IA dans des channels, comme avec des collègues.</text>
16+ <text x="640.0" y="200" font-size="26" fill="#1D1F24" text-anchor="middle" xml:space="preserve" font-weight="700">👋 Welcome to Sidekicks</text>
17+ <text x="640.0" y="234" font-size="15" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Talk with AI agents in channels, as you would with colleagues.</text>
1818 <rect x="240" y="280" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
1919 <circle cx="280" cy="318" r="18" fill="#3B6FE0"/>
2020 <text x="280" y="324" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">1</text>
21- <text x="316" y="312" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Ajoutez un agent</text>
22- <text x="316" y="336" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Un assistant qui tourne sur votre ordinateur, par exemple avec mm.</text>
21+ <text x="316" y="312" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Add an agent</text>
22+ <text x="316" y="336" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">An assistant that runs on your computer, for example with mm.</text>
2323 <rect x="240" y="372" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
2424 <circle cx="280" cy="410" r="18" fill="#3B6FE0"/>
2525 <text x="280" y="416" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">2</text>
26- <text x="316" y="404" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Ouvrez un channel</text>
27- <text x="316" y="428" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">#général est déjà prêt. Créez-en d'autres par sujet.</text>
26+ <text x="316" y="404" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Open a channel</text>
27+ <text x="316" y="428" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">#general is ready. Create others, one per topic.</text>
2828 <rect x="240" y="464" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
2929 <circle cx="280" cy="502" r="18" fill="#3B6FE0"/>
3030 <text x="280" y="508" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">3</text>
31- <text x="316" y="496" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Appelez-le avec @</text>
32- <text x="316" y="520" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">« @alice bonjour » : Alice rejoint le channel et vous répond.</text>
31+ <text x="316" y="496" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Call it with @</text>
32+ <text x="316" y="520" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">"@alice hello": Alice joins the channel and answers you.</text>
3333 <rect x="420" y="570" width="240" height="40" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
34- <text x="540.0" y="595.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">+ Ajouter mon premier agent</text>
34+ <text x="540.0" y="595.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">+ Add my first agent</text>
3535 <rect x="680" y="570" width="200" height="40" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
36- <text x="780.0" y="595.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Ouvrir une configuration…</text>
37- <text x="640.0" y="650" font-size="12" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)</text>
36+ <text x="780.0" y="595.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Open a configuration…</text>
37+ <text x="640.0" y="650" font-size="12" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)</text>
3838 <rect x="240" y="680" width="800" height="70" rx="10" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
39- <text x="260" y="708" font-size="14" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">La commande « mm » est introuvable.</text>
40- <text x="260" y="732" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent.</text>
39+ <text x="260" y="708" font-size="14" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">The command "mm" cannot be found.</text>
40+ <text x="260" y="732" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Install it (link to the guide) or choose another program for this agent.</text>
4141 <circle cx="1052" cy="312" r="12" fill="#6A1B9A"/>
4242 <text x="1052" y="316.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
4343 <circle cx="892" cy="590" r="12" fill="#6A1B9A"/>
@@ -49,25 +49,25 @@
4949 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
5050 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
5151 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
52- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Trois étapes, pas plus</text>
53- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">L'état vide explique le modèle mental :</text>
52+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Three steps, no more</text>
53+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The empty state explains the mental model:</text>
5454 <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">agents, channels, @ (#33).</text>
5555 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
5656 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
5757 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
58- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Un assistant plutôt que du YAML</text>
59- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le formulaire écrit le YAML à la place de</text>
60- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'utilisateur ; Settings reste accessible.</text>
58+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">An assistant rather than YAML</text>
59+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The form writes the YAML for the user;</text>
60+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Settings stays at hand.</text>
6161 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
6262 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
6363 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
64- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Rien à créer à la main</text>
65- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le fichier par défaut existe déjà, avec des</text>
66- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">commentaires (#9).</text>
64+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Nothing to create by hand</text>
65+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The default file already exists, with</text>
66+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">comments (#9).</text>
6767 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
6868 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
6969 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
70- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Erreurs humaines</text>
71- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Dire ce qui ne va pas et quoi faire ;</text>
72- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">jamais de trace technique (#33).</text>
70+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Human errors</text>
71+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Say what is wrong and what to do;</text>
72+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">never a technical trace (#33).</text>
7373 </svg>
@@ -1,8 +1,8 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">1 <svg xmlns="http://www.w3.org/2000/svg" width="1660" height="900" viewBox="0 0 1660 900" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>2 <defs><filter id="shadow" x="-10%" y="-10%" width="120%" height="120%"><feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#000" flood-opacity="0.18"/></filter></defs>
3 <rect width="1660" height="900" fill="#FFFFFF"/>3 <rect width="1660" height="900" fill="#FFFFFF"/>
4- <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">6 · Premier lancement</text>4+ <text x="20" y="38" font-size="24" font-weight="700" fill="#1A237E">6 · First launch</text>
5- <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Moins d'une minute pour savoir quoi faire, sans lire de documentation</text>5+ <text x="20" y="62" font-size="14" font-style="italic" fill="#546E7A">Less than a minute to know what to do, without reading any documentation</text>
6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>6 <g filter="url(#shadow)"><rect x="20" y="80" width="1240" height="780" rx="12" fill="#F6F6F7" stroke="#C9CBD1" stroke-width="1"/></g>
7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>7 <rect x="20" y="80" width="1240" height="36" rx="12" fill="#F0F1F3" stroke="none"/>
8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>8 <rect x="20" y="104" width="1240" height="12" rx="0" fill="#F0F1F3" stroke="none"/>
@@ -11,33 +11,33 @@
11 <circle cx="80" cy="98" r="6" fill="#28C840"/>11 <circle cx="80" cy="98" r="6" fill="#28C840"/>
12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>12 <text x="640.0" y="103" font-size="13" fill="#6B6F7A" text-anchor="middle" xml:space="preserve" font-weight="700">Sidekicks</text>
13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>13 <rect x="20" y="834" width="1240" height="26" rx="0" fill="#F0F1F3" stroke="none"/>
14- <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">○ aucun agent configuré · config : créée à l'emplacement par défaut</text>14+ <text x="32" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">○ no agent configured · config: created at the default location</text>
15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>15 <text x="1248" y="851" font-size="11.5" fill="#6B6F7A" text-anchor="end" xml:space="preserve">⚙︎ Settings ⌘K</text>
16- <text x="640.0" y="200" font-size="26" fill="#1D1F24" text-anchor="middle" xml:space="preserve" font-weight="700">👋 Bienvenue dans Sidekicks</text>16+ <text x="640.0" y="200" font-size="26" fill="#1D1F24" text-anchor="middle" xml:space="preserve" font-weight="700">👋 Welcome to Sidekicks</text>
17- <text x="640.0" y="234" font-size="15" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Discutez avec des agents d'IA dans des channels, comme avec des collègues.</text>17+ <text x="640.0" y="234" font-size="15" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Talk with AI agents in channels, as you would with colleagues.</text>
18 <rect x="240" y="280" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>18 <rect x="240" y="280" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
19 <circle cx="280" cy="318" r="18" fill="#3B6FE0"/>19 <circle cx="280" cy="318" r="18" fill="#3B6FE0"/>
20 <text x="280" y="324" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">1</text>20 <text x="280" y="324" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">1</text>
21- <text x="316" y="312" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Ajoutez un agent</text>21+ <text x="316" y="312" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Add an agent</text>
22- <text x="316" y="336" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">Un assistant qui tourne sur votre ordinateur, par exemple avec mm.</text>22+ <text x="316" y="336" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">An assistant that runs on your computer, for example with mm.</text>
23 <rect x="240" y="372" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>23 <rect x="240" y="372" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
24 <circle cx="280" cy="410" r="18" fill="#3B6FE0"/>24 <circle cx="280" cy="410" r="18" fill="#3B6FE0"/>
25 <text x="280" y="416" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">2</text>25 <text x="280" y="416" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">2</text>
26- <text x="316" y="404" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Ouvrez un channel</text>26+ <text x="316" y="404" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Open a channel</text>
27- <text x="316" y="428" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">#général est déjà prêt. Créez-en d'autres par sujet.</text>27+ <text x="316" y="428" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">#general is ready. Create others, one per topic.</text>
28 <rect x="240" y="464" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>28 <rect x="240" y="464" width="800" height="76" rx="12" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
29 <circle cx="280" cy="502" r="18" fill="#3B6FE0"/>29 <circle cx="280" cy="502" r="18" fill="#3B6FE0"/>
30 <text x="280" y="508" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">3</text>30 <text x="280" y="508" font-size="16" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">3</text>
31- <text x="316" y="496" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Appelez-le avec @</text>31+ <text x="316" y="496" font-size="16" fill="#1D1F24" text-anchor="start" xml:space="preserve" font-weight="700">Call it with @</text>
32- <text x="316" y="520" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">« @alice bonjour » : Alice rejoint le channel et vous répond.</text>32+ <text x="316" y="520" font-size="13.5" fill="#6B6F7A" text-anchor="start" xml:space="preserve">"@alice hello": Alice joins the channel and answers you.</text>
33 <rect x="420" y="570" width="240" height="40" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>33 <rect x="420" y="570" width="240" height="40" rx="6" fill="#3B6FE0" stroke="#3B6FE0" stroke-width="1"/>
34- <text x="540.0" y="595.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">+ Ajouter mon premier agent</text>34+ <text x="540.0" y="595.0" font-size="13" fill="#FFFFFF" text-anchor="middle" xml:space="preserve" font-weight="700">+ Add my first agent</text>
35 <rect x="680" y="570" width="200" height="40" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>35 <rect x="680" y="570" width="200" height="40" rx="6" fill="#FFFFFF" stroke="#E2E3E6" stroke-width="1"/>
36- <text x="780.0" y="595.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Ouvrir une configuration…</text>36+ <text x="780.0" y="595.0" font-size="13" fill="#1D1F24" text-anchor="middle" xml:space="preserve">Open a configuration…</text>
37- <text x="640.0" y="650" font-size="12" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)</text>37+ <text x="640.0" y="650" font-size="12" fill="#6B6F7A" text-anchor="middle" xml:space="preserve">Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)</text>
38 <rect x="240" y="680" width="800" height="70" rx="10" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>38 <rect x="240" y="680" width="800" height="70" rx="10" fill="#FDECEF" stroke="#D64561" stroke-width="1"/>
39- <text x="260" y="708" font-size="14" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">La commande « mm » est introuvable.</text>39+ <text x="260" y="708" font-size="14" fill="#D64561" text-anchor="start" xml:space="preserve" font-weight="700">The command "mm" cannot be found.</text>
40- <text x="260" y="732" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent.</text>40+ <text x="260" y="732" font-size="13" fill="#1D1F24" text-anchor="start" xml:space="preserve">Install it (link to the guide) or choose another program for this agent.</text>
41 <circle cx="1052" cy="312" r="12" fill="#6A1B9A"/>41 <circle cx="1052" cy="312" r="12" fill="#6A1B9A"/>
42 <text x="1052" y="316.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>42 <text x="1052" y="316.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
43 <circle cx="892" cy="590" r="12" fill="#6A1B9A"/>43 <circle cx="892" cy="590" r="12" fill="#6A1B9A"/>
@@ -49,25 +49,25 @@
49 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>49 <rect x="1284" y="80" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
50 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>50 <circle cx="1306" cy="102" r="12" fill="#6A1B9A"/>
51 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>51 <text x="1306" y="106.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">1</text>
52- <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Trois étapes, pas plus</text>52+ <text x="1328" y="107" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Three steps, no more</text>
53- <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">L'état vide explique le modèle mental :</text>53+ <text x="1300" y="130" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The empty state explains the mental model:</text>
54 <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">agents, channels, @ (#33).</text>54 <text x="1300" y="148" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">agents, channels, @ (#33).</text>
55 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>55 <rect x="1284" y="170" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
56 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>56 <circle cx="1306" cy="192" r="12" fill="#6A1B9A"/>
57 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>57 <text x="1306" y="196.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">2</text>
58- <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Un assistant plutôt que du YAML</text>58+ <text x="1328" y="197" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">An assistant rather than YAML</text>
59- <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le formulaire écrit le YAML à la place de</text>59+ <text x="1300" y="220" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The form writes the YAML for the user;</text>
60- <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">l'utilisateur ; Settings reste accessible.</text>60+ <text x="1300" y="238" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Settings stays at hand.</text>
61 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>61 <rect x="1284" y="260" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
62 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>62 <circle cx="1306" cy="282" r="12" fill="#6A1B9A"/>
63 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>63 <text x="1306" y="286.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">3</text>
64- <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Rien à créer à la main</text>64+ <text x="1328" y="287" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Nothing to create by hand</text>
65- <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Le fichier par défaut existe déjà, avec des</text>65+ <text x="1300" y="310" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">The default file already exists, with</text>
66- <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">commentaires (#9).</text>66+ <text x="1300" y="328" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">comments (#9).</text>
67 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>67 <rect x="1284" y="350" width="356" height="76" rx="10" fill="#F3E5F5" stroke="#6A1B9A" stroke-width="1"/>
68 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>68 <circle cx="1306" cy="372" r="12" fill="#6A1B9A"/>
69 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>69 <text x="1306" y="376.5" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">4</text>
70- <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Erreurs humaines</text>70+ <text x="1328" y="377" font-size="13.5" fill="#4A148C" text-anchor="start" xml:space="preserve" font-weight="700">Human errors</text>
71- <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Dire ce qui ne va pas et quoi faire ;</text>71+ <text x="1300" y="400" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">Say what is wrong and what to do;</text>
72- <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">jamais de trace technique (#33).</text>72+ <text x="1300" y="418" font-size="12" fill="#4A148C" text-anchor="start" xml:space="preserve">never a technical trace (#33).</text>
73 </svg>73 </svg>
modified docs/design/README.md +26 -6
@@ -1,15 +1,15 @@
11 # Wireframes
22
3-Six screens for issue [#6](../../.tickets/issues/0006-maquettes-ux-des-panels-pour-le-citizen-user.yaml), drawn for a user who is not a developer. Each screen shows the window on the left and numbered design notes on the right; the purple badges point at what each note explains. The interface text is in French, the default language.
3+Six screens for issue [#6](../../.tickets/issues/0006-maquettes-ux-des-panels-pour-le-citizen-user.yaml), drawn for a user who is not a developer. Each screen shows the window on the left and numbered design notes on the right; the purple badges point at what each note explains. The interface text is in English, the default language (French is chosen with `ui.language: fr`).
44
55 | Screen | Shows |
66 | --- | --- |
7-| [1 · Layout global](01-layout.png) | Docks: channels and agents on the left, the conversation in the centre, Settings on the right, terminal and files folded at the bottom |
8-| [2 · Conversation multi-agents](02-conversation.png) | One colour per agent, the "joined the channel" message, folded tool calls, the Stop button, the context gauge |
9-| [3 · @ et /](03-mentions-commands.png) | The `@` agent list and the `/agent:command` list, Tab to pick |
7+| [1 · Overall layout](01-layout.png) | Docks: channels and agents on the left, the conversation in the centre, Settings on the right, terminal and files folded at the bottom |
8+| [2 · Multi-agent conversation](02-conversation.png) | One colour per agent, the "joined the channel" message, folded tool calls, the Stop button, the context gauge |
9+| [3 · @ and /](03-mentions-commands.png) | The `@` agent list and the `/agent:command` list, Tab to pick |
1010 | [4 · Permission](04-permission.png) | A permission request in plain language, the four ACP options and "Stop Riker" |
11-| [5 · Panel Agents](05-agents-panel.png) | An agent's state, workspace tree, editor and terminal |
12-| [6 · Premier lancement](06-first-launch.png) | The empty state: three steps, an "add my first agent" assistant, a human error message |
11+| [5 · Agents panel](05-agents-panel.png) | An agent's state, workspace tree, editor and terminal |
12+| [6 · First launch](06-first-launch.png) | The empty state: three steps, an "add my first agent" assistant, a human error message |
1313
1414 ## Files
1515
@@ -26,6 +26,26 @@ for f in docs/design/*.svg; do rsvg-convert -o "${f%.svg}.png" "$f"; done
2626
2727 Editing `wireframes.drawio` by hand is fine for exploring, but the next generation overwrites it: carry the change over into `wireframes.py`.
2828
29+## Logo
30+
31+The logo is **Orbite**: you in the centre, your agents in orbit around you. Colours: ink `#1E2240`, ring `#C9CCE8`, teal `#12A39A` and coral `#FF7A59` on a light ground; white, `#4A5078`, `#2FD1C5` and coral on a dark one.
32+
33+| File | Use |
34+| --- | --- |
35+| [logo/sidekicks-mark.svg](logo/sidekicks-mark.svg) | The mark on a light ground |
36+| [logo/sidekicks-mark-dark.svg](logo/sidekicks-mark-dark.svg) | The mark on a dark ground |
37+| [logo/sidekicks-favicon.svg](logo/sidekicks-favicon.svg) | 16–32 px: the ring is dropped, it no longer reads at that size |
38+| [logo/sidekicks-app-icon.svg](logo/sidekicks-app-icon.svg) | The application icon, a copy of `build/appicon.svg` |
39+
40+The application icon's source is `build/appicon.svg`. After changing it, regenerate the images:
41+
42+```bash
43+rsvg-convert -w 1024 -h 1024 -o build/appicon.png build/appicon.svg
44+cd build && wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin
45+```
46+
47+The last two options rebuild `build/darwin/Assets.car` from `build/appicon.icon` (its glyph is `Assets/sidekicks-mark.svg`); they only work on macOS.
48+
2949 ## Decisions taken in these screens
3050
3151 - Only the conversation and the left dock are visible by default; Settings, terminal and files are one click away (screens 1, 5).
@@ -1,15 +1,15 @@
1 # Wireframes1 # Wireframes
2 2
3-Six screens for issue [#6](../../.tickets/issues/0006-maquettes-ux-des-panels-pour-le-citizen-user.yaml), drawn for a user who is not a developer. Each screen shows the window on the left and numbered design notes on the right; the purple badges point at what each note explains. The interface text is in French, the default language.3+Six screens for issue [#6](../../.tickets/issues/0006-maquettes-ux-des-panels-pour-le-citizen-user.yaml), drawn for a user who is not a developer. Each screen shows the window on the left and numbered design notes on the right; the purple badges point at what each note explains. The interface text is in English, the default language (French is chosen with `ui.language: fr`).
4 4
5 | Screen | Shows |5 | Screen | Shows |
6 | --- | --- |6 | --- | --- |
7-| [1 · Layout global](01-layout.png) | Docks: channels and agents on the left, the conversation in the centre, Settings on the right, terminal and files folded at the bottom |7+| [1 · Overall layout](01-layout.png) | Docks: channels and agents on the left, the conversation in the centre, Settings on the right, terminal and files folded at the bottom |
8-| [2 · Conversation multi-agents](02-conversation.png) | One colour per agent, the "joined the channel" message, folded tool calls, the Stop button, the context gauge |8+| [2 · Multi-agent conversation](02-conversation.png) | One colour per agent, the "joined the channel" message, folded tool calls, the Stop button, the context gauge |
9-| [3 · @ et /](03-mentions-commands.png) | The `@` agent list and the `/agent:command` list, Tab to pick |9+| [3 · @ and /](03-mentions-commands.png) | The `@` agent list and the `/agent:command` list, Tab to pick |
10 | [4 · Permission](04-permission.png) | A permission request in plain language, the four ACP options and "Stop Riker" |10 | [4 · Permission](04-permission.png) | A permission request in plain language, the four ACP options and "Stop Riker" |
11-| [5 · Panel Agents](05-agents-panel.png) | An agent's state, workspace tree, editor and terminal |11+| [5 · Agents panel](05-agents-panel.png) | An agent's state, workspace tree, editor and terminal |
12-| [6 · Premier lancement](06-first-launch.png) | The empty state: three steps, an "add my first agent" assistant, a human error message |12+| [6 · First launch](06-first-launch.png) | The empty state: three steps, an "add my first agent" assistant, a human error message |
13 13
14 ## Files14 ## Files
15 15
@@ -26,6 +26,26 @@ for f in docs/design/*.svg; do rsvg-convert -o "${f%.svg}.png" "$f"; done
26 26
27 Editing `wireframes.drawio` by hand is fine for exploring, but the next generation overwrites it: carry the change over into `wireframes.py`.27 Editing `wireframes.drawio` by hand is fine for exploring, but the next generation overwrites it: carry the change over into `wireframes.py`.
28 28
29+## Logo
30+
31+The logo is **Orbite**: you in the centre, your agents in orbit around you. Colours: ink `#1E2240`, ring `#C9CCE8`, teal `#12A39A` and coral `#FF7A59` on a light ground; white, `#4A5078`, `#2FD1C5` and coral on a dark one.
32+
33+| File | Use |
34+| --- | --- |
35+| [logo/sidekicks-mark.svg](logo/sidekicks-mark.svg) | The mark on a light ground |
36+| [logo/sidekicks-mark-dark.svg](logo/sidekicks-mark-dark.svg) | The mark on a dark ground |
37+| [logo/sidekicks-favicon.svg](logo/sidekicks-favicon.svg) | 16–32 px: the ring is dropped, it no longer reads at that size |
38+| [logo/sidekicks-app-icon.svg](logo/sidekicks-app-icon.svg) | The application icon, a copy of `build/appicon.svg` |
39+
40+The application icon's source is `build/appicon.svg`. After changing it, regenerate the images:
41+
42+```bash
43+rsvg-convert -w 1024 -h 1024 -o build/appicon.png build/appicon.svg
44+cd build && wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin
45+```
46+
47+The last two options rebuild `build/darwin/Assets.car` from `build/appicon.icon` (its glyph is `Assets/sidekicks-mark.svg`); they only work on macOS.
48+
29 ## Decisions taken in these screens49 ## Decisions taken in these screens
30 50
31 - Only the conversation and the left dock are visible by default; Settings, terminal and files are one click away (screens 1, 5).51 - Only the conversation and the left dock are visible by default; Settings, terminal and files are one click away (screens 1, 5).
added docs/design/logo/sidekicks-app-icon.svg +15 -0
new file mode 100644
@@ -0,0 +1,15 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
3+ <rect x="100" y="100" width="824" height="824" rx="185" fill="#1E2240"/>
4+ <svg x="212" y="212" width="600" height="600" viewBox="0 0 100 100">
5+ <mask id="cut">
6+ <rect width="100" height="100" fill="#fff"/>
7+ <circle cx="76" cy="28" r="12" fill="#000"/>
8+ <circle cx="18" cy="39" r="10" fill="#000"/>
9+ </mask>
10+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
11+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
12+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
13+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
14+ </svg>
15+</svg>
new file mode 100644
@@ -0,0 +1,15 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
3+ <rect x="100" y="100" width="824" height="824" rx="185" fill="#1E2240"/>
4+ <svg x="212" y="212" width="600" height="600" viewBox="0 0 100 100">
5+ <mask id="cut">
6+ <rect width="100" height="100" fill="#fff"/>
7+ <circle cx="76" cy="28" r="12" fill="#000"/>
8+ <circle cx="18" cy="39" r="10" fill="#000"/>
9+ </mask>
10+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
11+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
12+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
13+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
14+ </svg>
15+</svg>
added docs/design/logo/sidekicks-favicon.svg +6 -0
new file mode 100644
@@ -0,0 +1,6 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <circle cx="50" cy="50" r="20" fill="#1E2240"/>
4+ <circle cx="78" cy="26" r="14" fill="#12A39A"/>
5+ <circle cx="18" cy="40" r="12" fill="#FF7A59"/>
6+</svg>
new file mode 100644
@@ -0,0 +1,6 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <circle cx="50" cy="50" r="20" fill="#1E2240"/>
4+ <circle cx="78" cy="26" r="14" fill="#12A39A"/>
5+ <circle cx="18" cy="40" r="12" fill="#FF7A59"/>
6+</svg>
added docs/design/logo/sidekicks-mark-dark.svg +12 -0
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
10+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#4A5078" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#FFFFFF"/>
10+ <circle cx="76" cy="28" r="9" fill="#2FD1C5"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
added docs/design/logo/sidekicks-mark.svg +12 -0
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#C9CCE8" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#1E2240"/>
10+ <circle cx="76" cy="28" r="9" fill="#12A39A"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
new file mode 100644
@@ -0,0 +1,12 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100" role="img" aria-label="Sidekicks">
3+ <mask id="cut">
4+ <rect width="100" height="100" fill="#fff"/>
5+ <circle cx="76" cy="28" r="12" fill="#000"/>
6+ <circle cx="18" cy="39" r="10" fill="#000"/>
7+ </mask>
8+ <circle cx="50" cy="50" r="35" fill="none" stroke="#C9CCE8" stroke-width="4" mask="url(#cut)"/>
9+ <circle cx="50" cy="50" r="15" fill="#1E2240"/>
10+ <circle cx="76" cy="28" r="9" fill="#12A39A"/>
11+ <circle cx="18" cy="39" r="7" fill="#FF7A59"/>
12+</svg>
modified docs/design/wireframes.drawio +199 -199
@@ -1,10 +1,10 @@
11 <mxfile host="app.diagrams.net">
2- <diagram id="01-layout" name="1 · Layout global">
2+ <diagram id="01-layout" name="1 · Overall layout">
33 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
44 <root>
55 <mxCell id="0"/>
66 <mxCell id="1" parent="0"/>
7- <mxCell id="01-layout-title" value="&lt;b&gt;1 · Layout global&lt;/b&gt;&lt;br&gt;&lt;i&gt;Docks façon Zed, mais seul l'essentiel est visible par défaut&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
7+ <mxCell id="01-layout-title" value="&lt;b&gt;1 · Overall layout&lt;/b&gt;&lt;br&gt;&lt;i&gt;Zed-like docks, but only the essentials are visible by default&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
88 <mxCell id="01-layout-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
99 <mxCell id="01-layout-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
1010 <mxCell id="01-layout-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -13,14 +13,14 @@
1313 <mxCell id="01-layout-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
1414 <mxCell id="01-layout-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
1515 <mxCell id="01-layout-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
16- <mxCell id="01-layout-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>
16+ <mxCell id="01-layout-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
1717 <mxCell id="01-layout-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
1818 <mxCell id="01-layout-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
1919 <mxCell id="01-layout-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
2020 <mxCell id="01-layout-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
2121 <mxCell id="01-layout-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
22- <mxCell id="01-layout-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
23- <mxCell id="01-layout-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
22+ <mxCell id="01-layout-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
23+ <mxCell id="01-layout-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
2424 <mxCell id="01-layout-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
2525 <mxCell id="01-layout-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
2626 <mxCell id="01-layout-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -32,18 +32,18 @@
3232 <mxCell id="01-layout-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
3333 <mxCell id="01-layout-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
3434 <mxCell id="01-layout-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="709" height="44" as="geometry"/></mxCell>
35- <mxCell id="01-layout-28" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
35+ <mxCell id="01-layout-28" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
3636 <mxCell id="01-layout-29" value="👩 Alice · 🧔 Bob" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="830.9" y="131" width="113.1" height="18" as="geometry"/></mxCell>
37- <mxCell id="01-layout-30" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>
38- <mxCell id="01-layout-31" value="@alice résume-moi ce que fait ACP en deux phrases" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="383.66999999999996" height="18.5" as="geometry"/></mxCell>
37+ <mxCell id="01-layout-30" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="23.49" height="18.5" as="geometry"/></mxCell>
38+ <mxCell id="01-layout-31" value="@alice sum up what ACP does in two sentences" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="344.52" height="18.5" as="geometry"/></mxCell>
3939 <mxCell id="01-layout-32" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
40- <mxCell id="01-layout-33" value="ACP est un protocole qui permet à une application de dialoguer avec" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="260.5" width="524.61" height="18.5" as="geometry"/></mxCell>
41- <mxCell id="01-layout-34" value="n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="280.0" width="548.0999999999999" height="18.5" as="geometry"/></mxCell>
40+ <mxCell id="01-layout-33" value="ACP is a protocol that lets an application talk with any AI agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="260.5" width="516.78" height="18.5" as="geometry"/></mxCell>
41+ <mxCell id="01-layout-34" value="Sidekicks uses it to stay agent-agnostic." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="280.0" width="321.03" height="18.5" as="geometry"/></mxCell>
4242 <mxCell id="01-layout-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="724" width="669" height="64" as="geometry"/></mxCell>
43- <mxCell id="01-layout-36" value="Écrivez un message… (@ pour un agent, / pour une commande)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="736" width="479.08" height="19" as="geometry"/></mxCell>
44- <mxCell id="01-layout-37" value="Entrée pour envoyer · Maj+Entrée nouvelle ligne" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="763" width="299.85999999999996" height="16" as="geometry"/></mxCell>
43+ <mxCell id="01-layout-36" value="Write a message… (@ for an agent, / for a command)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="736" width="414.11999999999995" height="19" as="geometry"/></mxCell>
44+ <mxCell id="01-layout-37" value="Enter to send · Shift+Enter for a new line" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="763" width="267.96" height="16" as="geometry"/></mxCell>
4545 <mxCell id="01-layout-38" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="960" y="116" width="300" height="718" as="geometry"/></mxCell>
46- <mxCell id="01-layout-39" value="⚙︎ Settings — equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="131" width="188.5" height="18" as="geometry"/></mxCell>
46+ <mxCell id="01-layout-39" value="⚙︎ Settings — team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="131" width="173.42" height="18" as="geometry"/></mxCell>
4747 <mxCell id="01-layout-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="160" width="280" height="300" as="geometry"/></mxCell>
4848 <mxCell id="01-layout-41" value="version: 1" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="172" width="74.4" height="17" as="geometry"/></mxCell>
4949 <mxCell id="01-layout-42" value="agents:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="191" width="52.08" height="17" as="geometry"/></mxCell>
@@ -52,13 +52,13 @@
5252 <mxCell id="01-layout-45" value=" command: mm" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="248" width="111.6" height="17" as="geometry"/></mxCell>
5353 <mxCell id="01-layout-46" value=" args: [-acp, ...]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="267" width="156.24" height="17" as="geometry"/></mxCell>
5454 <mxCell id="01-layout-47" value="channels:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="286" width="66.96" height="17" as="geometry"/></mxCell>
55- <mxCell id="01-layout-48" value=" - name: général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="305" width="126.48" height="17" as="geometry"/></mxCell>
55+ <mxCell id="01-layout-48" value=" - name: general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="305" width="126.48" height="17" as="geometry"/></mxCell>
5656 <mxCell id="01-layout-49" value=" agents: [alice, bob]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="324" width="178.56" height="17" as="geometry"/></mxCell>
57- <mxCell id="01-layout-50" value="✓ Configuration valide" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3E9B4F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="477.5" width="159.5" height="17.5" as="geometry"/></mxCell>
57+ <mxCell id="01-layout-50" value="✓ Valid configuration" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3E9B4F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="477.5" width="152.25" height="17.5" as="geometry"/></mxCell>
5858 <mxCell id="01-layout-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="508" width="120" height="32" as="geometry"/></mxCell>
59- <mxCell id="01-layout-52" value="Enregistrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="988.53" y="516.0" width="82.94" height="18" as="geometry"/></mxCell>
59+ <mxCell id="01-layout-52" value="Save" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1014.92" y="516.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
6060 <mxCell id="01-layout-53" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="804" width="709" height="30" as="geometry"/></mxCell>
61- <mxCell id="01-layout-54" value="▸ Terminal · Fichiers (repliés)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="812" width="229.67999999999998" height="17" as="geometry"/></mxCell>
61+ <mxCell id="01-layout-54" value="▸ Terminal · Files (folded)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="812" width="201.83999999999997" height="17" as="geometry"/></mxCell>
6262 <mxCell id="01-layout-55" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="208" y="130" width="24" height="24" as="geometry"/></mxCell>
6363 <mxCell id="01-layout-56" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="359" y="228" width="24" height="24" as="geometry"/></mxCell>
6464 <mxCell id="01-layout-57" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1218" y="128" width="24" height="24" as="geometry"/></mxCell>
@@ -66,39 +66,39 @@
6666 <mxCell id="01-layout-59" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="428" y="835" width="24" height="24" as="geometry"/></mxCell>
6767 <mxCell id="01-layout-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
6868 <mxCell id="01-layout-61" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
69- <mxCell id="01-layout-62" value="Dock gauche : channels et agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="250.55999999999997" height="18.5" as="geometry"/></mxCell>
70- <mxCell id="01-layout-63" value="Toujours visible. La pastille donne l'état de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="313.2" height="17" as="geometry"/></mxCell>
71- <mxCell id="01-layout-64" value="l'agent : vert prêt, orange occupé, gris arrêté." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="334.08" height="17" as="geometry"/></mxCell>
69+ <mxCell id="01-layout-62" value="Left dock: channels and agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
70+ <mxCell id="01-layout-63" value="Always visible. The dot gives the agent's" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="285.35999999999996" height="17" as="geometry"/></mxCell>
71+ <mxCell id="01-layout-64" value="state: green ready, orange busy, grey stopped." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="320.15999999999997" height="17" as="geometry"/></mxCell>
7272 <mxCell id="01-layout-65" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
7373 <mxCell id="01-layout-66" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
74- <mxCell id="01-layout-67" value="Centre : la conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="187.92" height="18.5" as="geometry"/></mxCell>
75- <mxCell id="01-layout-68" value="Le seul panel indispensable. Pleine largeur" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="299.28" height="17" as="geometry"/></mxCell>
76- <mxCell id="01-layout-69" value="quand les docks sont repliés." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="201.83999999999997" height="17" as="geometry"/></mxCell>
74+ <mxCell id="01-layout-67" value="Centre: the conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="187.92" height="18.5" as="geometry"/></mxCell>
75+ <mxCell id="01-layout-68" value="The only panel you cannot do without." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="257.52" height="17" as="geometry"/></mxCell>
76+ <mxCell id="01-layout-69" value="Full width when the docks are folded." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="257.52" height="17" as="geometry"/></mxCell>
7777 <mxCell id="01-layout-70" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>
7878 <mxCell id="01-layout-71" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
79- <mxCell id="01-layout-72" value="Dock droit : Settings" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>
80- <mxCell id="01-layout-73" value="Éditeur YAML (CodeMirror) avec validation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="285.35999999999996" height="17" as="geometry"/></mxCell>
81- <mxCell id="01-layout-74" value="en direct. Fermé par défaut ; s'ouvre" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="257.52" height="17" as="geometry"/></mxCell>
82- <mxCell id="01-layout-75" value="par ⚙︎ ou ⌘,." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="90.47999999999999" height="17" as="geometry"/></mxCell>
79+ <mxCell id="01-layout-72" value="Right dock: Settings" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="156.6" height="18.5" as="geometry"/></mxCell>
80+ <mxCell id="01-layout-73" value="YAML editor (CodeMirror) with live" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="236.64" height="17" as="geometry"/></mxCell>
81+ <mxCell id="01-layout-74" value="validation. Closed by default; opens" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="250.55999999999997" height="17" as="geometry"/></mxCell>
82+ <mxCell id="01-layout-75" value="with ⚙︎ or ⌘,." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="97.44" height="17" as="geometry"/></mxCell>
8383 <mxCell id="01-layout-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>
8484 <mxCell id="01-layout-77" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>
85- <mxCell id="01-layout-78" value="Dock bas : terminal et fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="242.73" height="18.5" as="geometry"/></mxCell>
86- <mxCell id="01-layout-79" value="Replié par défaut : utile aux curieux," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="264.47999999999996" height="17" as="geometry"/></mxCell>
87- <mxCell id="01-layout-80" value="jamais imposé au citizen user." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="208.79999999999998" height="17" as="geometry"/></mxCell>
85+ <mxCell id="01-layout-78" value="Bottom dock: terminal and files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="242.73" height="18.5" as="geometry"/></mxCell>
86+ <mxCell id="01-layout-79" value="Folded by default: there for the curious," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="285.35999999999996" height="17" as="geometry"/></mxCell>
87+ <mxCell id="01-layout-80" value="never forced on the citizen user." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="229.67999999999998" height="17" as="geometry"/></mxCell>
8888 <mxCell id="01-layout-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="76" as="geometry"/></mxCell>
8989 <mxCell id="01-layout-82" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>
90- <mxCell id="01-layout-83" value="Barre d'état" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="93.96" height="18.5" as="geometry"/></mxCell>
91- <mxCell id="01-layout-84" value="Rappelle la limite d'agents simultanés" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="264.47999999999996" height="17" as="geometry"/></mxCell>
92- <mxCell id="01-layout-85" value="(mémoire) et la configuration ouverte." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="264.47999999999996" height="17" as="geometry"/></mxCell>
90+ <mxCell id="01-layout-83" value="Status bar" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="78.3" height="18.5" as="geometry"/></mxCell>
91+ <mxCell id="01-layout-84" value="Shows the limit of agents running at once" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="285.35999999999996" height="17" as="geometry"/></mxCell>
92+ <mxCell id="01-layout-85" value="(memory) and the open configuration." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="250.55999999999997" height="17" as="geometry"/></mxCell>
9393 </root>
9494 </mxGraphModel>
9595 </diagram>
96- <diagram id="02-conversation" name="2 · Conversation multi-agents">
96+ <diagram id="02-conversation" name="2 · Multi-agent conversation">
9797 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
9898 <root>
9999 <mxCell id="0"/>
100100 <mxCell id="1" parent="0"/>
101- <mxCell id="02-conversation-title" value="&lt;b&gt;2 · Conversation multi-agents&lt;/b&gt;&lt;br&gt;&lt;i&gt;Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
101+ <mxCell id="02-conversation-title" value="&lt;b&gt;2 · Multi-agent conversation&lt;/b&gt;&lt;br&gt;&lt;i&gt;Each agent has its colour; tools are folded; you can always stop&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
102102 <mxCell id="02-conversation-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
103103 <mxCell id="02-conversation-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
104104 <mxCell id="02-conversation-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -107,14 +107,14 @@
107107 <mxCell id="02-conversation-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
108108 <mxCell id="02-conversation-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
109109 <mxCell id="02-conversation-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
110- <mxCell id="02-conversation-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>
110+ <mxCell id="02-conversation-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
111111 <mxCell id="02-conversation-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
112112 <mxCell id="02-conversation-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
113113 <mxCell id="02-conversation-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
114114 <mxCell id="02-conversation-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
115115 <mxCell id="02-conversation-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
116- <mxCell id="02-conversation-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
117- <mxCell id="02-conversation-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
116+ <mxCell id="02-conversation-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
117+ <mxCell id="02-conversation-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
118118 <mxCell id="02-conversation-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
119119 <mxCell id="02-conversation-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
120120 <mxCell id="02-conversation-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -126,31 +126,31 @@
126126 <mxCell id="02-conversation-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
127127 <mxCell id="02-conversation-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
128128 <mxCell id="02-conversation-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>
129- <mxCell id="02-conversation-28" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
129+ <mxCell id="02-conversation-28" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
130130 <mxCell id="02-conversation-29" value="👩 Alice · 🧔 Bob · 🧑‍🚀 Riker" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1040.42" y="131" width="203.57999999999998" height="18" as="geometry"/></mxCell>
131- <mxCell id="02-conversation-30" value="— Riker a rejoint le channel —" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="275" y="183.5" width="217.49999999999997" height="17.5" as="geometry"/></mxCell>
132- <mxCell id="02-conversation-31" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="214.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>
133- <mxCell id="02-conversation-32" value="@alice propose un nom pour un script qui sauvegarde mes photos" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="236.5" width="485.46" height="18.5" as="geometry"/></mxCell>
131+ <mxCell id="02-conversation-30" value="— Riker joined the channel —" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="275" y="183.5" width="203.0" height="17.5" as="geometry"/></mxCell>
132+ <mxCell id="02-conversation-31" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="214.5" width="23.49" height="18.5" as="geometry"/></mxCell>
133+ <mxCell id="02-conversation-32" value="@alice suggest a name for a script that backs up my photos" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="236.5" width="454.14" height="18.5" as="geometry"/></mxCell>
134134 <mxCell id="02-conversation-33" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="270.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
135- <mxCell id="02-conversation-34" value="Je propose « photo-backup.sh » : court, explicite, et l'extension dit que" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="292.5" width="571.5899999999999" height="18.5" as="geometry"/></mxCell>
136- <mxCell id="02-conversation-35" value="c'est un script shell." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="312.0" width="172.26" height="18.5" as="geometry"/></mxCell>
137- <mxCell id="02-conversation-36" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="346.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>
138- <mxCell id="02-conversation-37" value="@bob tu valides ? @riker crée le fichier vide" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="368.5" width="352.34999999999997" height="18.5" as="geometry"/></mxCell>
135+ <mxCell id="02-conversation-34" value="I suggest &quot;photo-backup.sh&quot;: short, clear, and the extension says" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="292.5" width="508.95" height="18.5" as="geometry"/></mxCell>
136+ <mxCell id="02-conversation-35" value="it is a shell script." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="312.0" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>
137+ <mxCell id="02-conversation-36" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="346.5" width="23.49" height="18.5" as="geometry"/></mxCell>
138+ <mxCell id="02-conversation-37" value="@bob do you agree? @riker create the empty file" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="368.5" width="368.01" height="18.5" as="geometry"/></mxCell>
139139 <mxCell id="02-conversation-38" value="🧔 Bob" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#00838F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="402.5" width="39.15" height="18.5" as="geometry"/></mxCell>
140- <mxCell id="02-conversation-39" value="Bon choix. Alice a raison : un nom en minuscules, sans espace." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="424.5" width="485.46" height="18.5" as="geometry"/></mxCell>
140+ <mxCell id="02-conversation-39" value="Good choice. Alice is right: a lowercase name, no spaces." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="424.5" width="446.30999999999995" height="18.5" as="geometry"/></mxCell>
141141 <mxCell id="02-conversation-40" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="458.5" width="70.47" height="18.5" as="geometry"/></mxCell>
142- <mxCell id="02-conversation-41" value="travaille…" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="459.5" width="72.5" height="17.5" as="geometry"/></mxCell>
142+ <mxCell id="02-conversation-41" value="working…" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="459.5" width="57.99999999999999" height="17.5" as="geometry"/></mxCell>
143143 <mxCell id="02-conversation-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="484" width="560" height="40" as="geometry"/></mxCell>
144- <mxCell id="02-conversation-43" value="▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="496.5" width="434.99999999999994" height="17.5" as="geometry"/></mxCell>
144+ <mxCell id="02-conversation-43" value="▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="496.5" width="406.0" height="17.5" as="geometry"/></mxCell>
145145 <mxCell id="02-conversation-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="532" width="560" height="40" as="geometry"/></mxCell>
146- <mxCell id="02-conversation-45" value="▸ 🛠 bash : ls -la … en cours" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="544.5" width="449.49999999999994" height="17.5" as="geometry"/></mxCell>
146+ <mxCell id="02-conversation-45" value="▸ 🛠 bash: ls -la … running" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="544.5" width="434.99999999999994" height="17.5" as="geometry"/></mxCell>
147147 <mxCell id="02-conversation-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="754" width="849" height="64" as="geometry"/></mxCell>
148148 <mxCell id="02-conversation-47" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="766" width="20" height="19" as="geometry"/></mxCell>
149- <mxCell id="02-conversation-48" value="Entrée pour envoyer · Maj+Entrée nouvelle ligne" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="793" width="299.85999999999996" height="16" as="geometry"/></mxCell>
149+ <mxCell id="02-conversation-48" value="Enter to send · Shift+Enter for a new line" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="793" width="267.96" height="16" as="geometry"/></mxCell>
150150 <mxCell id="02-conversation-49" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1132" y="770" width="108" height="32" as="geometry"/></mxCell>
151- <mxCell id="02-conversation-50" value="■ Arrêter" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1152.07" y="778.0" width="67.86" height="18" as="geometry"/></mxCell>
151+ <mxCell id="02-conversation-50" value="■ Stop" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1163.38" y="778.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
152152 <mxCell id="02-conversation-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=30;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1040" y="176" width="200" height="30" as="geometry"/></mxCell>
153- <mxCell id="02-conversation-52" value="contexte Riker : 41 %" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1069.965" y="184.5" width="140.07" height="16.5" as="geometry"/></mxCell>
153+ <mxCell id="02-conversation-52" value="Riker's context: 41%" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1073.3" y="184.5" width="133.39999999999998" height="16.5" as="geometry"/></mxCell>
154154 <mxCell id="02-conversation-53" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="563" y="179" width="24" height="24" as="geometry"/></mxCell>
155155 <mxCell id="02-conversation-54" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="343" y="262" width="24" height="24" as="geometry"/></mxCell>
156156 <mxCell id="02-conversation-55" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="843" y="492" width="24" height="24" as="geometry"/></mxCell>
@@ -158,40 +158,40 @@
158158 <mxCell id="02-conversation-57" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1016" y="179" width="24" height="24" as="geometry"/></mxCell>
159159 <mxCell id="02-conversation-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
160160 <mxCell id="02-conversation-59" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
161- <mxCell id="02-conversation-60" value="Arrivée visible" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="117.44999999999999" height="18.5" as="geometry"/></mxCell>
162- <mxCell id="02-conversation-61" value="La 1re mention invite l'agent : un message" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>
163- <mxCell id="02-conversation-62" value="système le dit. On sait qui « écoute »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="271.44" height="17" as="geometry"/></mxCell>
161+ <mxCell id="02-conversation-60" value="A visible arrival" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>
162+ <mxCell id="02-conversation-61" value="The first mention invites the agent: a system" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="313.2" height="17" as="geometry"/></mxCell>
163+ <mxCell id="02-conversation-62" value="message says so. You know who is listening." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="299.28" height="17" as="geometry"/></mxCell>
164164 <mxCell id="02-conversation-63" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
165165 <mxCell id="02-conversation-64" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
166- <mxCell id="02-conversation-65" value="Une couleur par agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>
167- <mxCell id="02-conversation-66" value="Avatar + nom colorés, réponses en Markdown" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="292.32" height="17" as="geometry"/></mxCell>
168- <mxCell id="02-conversation-67" value="avec code coloré (ADR 0002)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="194.88" height="17" as="geometry"/></mxCell>
166+ <mxCell id="02-conversation-65" value="One colour per agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="156.6" height="18.5" as="geometry"/></mxCell>
167+ <mxCell id="02-conversation-66" value="Coloured avatar and name, answers in" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="250.55999999999997" height="17" as="geometry"/></mxCell>
168+ <mxCell id="02-conversation-67" value="Markdown with highlighted code (ADR 0002)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="292.32" height="17" as="geometry"/></mxCell>
169169 <mxCell id="02-conversation-68" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>
170170 <mxCell id="02-conversation-69" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
171- <mxCell id="02-conversation-70" value="Outils repliés" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
172- <mxCell id="02-conversation-71" value="Une ligne par tool call : titre lisible et" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="292.32" height="17" as="geometry"/></mxCell>
173- <mxCell id="02-conversation-72" value="statut. Déplier montre la commande, le" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>
174- <mxCell id="02-conversation-73" value="diff ou la sortie." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="125.27999999999999" height="17" as="geometry"/></mxCell>
171+ <mxCell id="02-conversation-70" value="Folded tools" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="93.96" height="18.5" as="geometry"/></mxCell>
172+ <mxCell id="02-conversation-71" value="One line per tool call: a readable title" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="278.4" height="17" as="geometry"/></mxCell>
173+ <mxCell id="02-conversation-72" value="and its status. Unfolding shows the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="243.6" height="17" as="geometry"/></mxCell>
174+ <mxCell id="02-conversation-73" value="command, the diff or the output." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="222.71999999999997" height="17" as="geometry"/></mxCell>
175175 <mxCell id="02-conversation-74" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>
176176 <mxCell id="02-conversation-75" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>
177- <mxCell id="02-conversation-76" value="Toujours un bouton Arrêter" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="203.57999999999998" height="18.5" as="geometry"/></mxCell>
178- <mxCell id="02-conversation-77" value="Envoie session/cancel aux agents qui" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="250.55999999999997" height="17" as="geometry"/></mxCell>
179- <mxCell id="02-conversation-78" value="répondent dans ce channel (ADR 0001)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="257.52" height="17" as="geometry"/></mxCell>
177+ <mxCell id="02-conversation-76" value="Always a Stop button" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="156.6" height="18.5" as="geometry"/></mxCell>
178+ <mxCell id="02-conversation-77" value="Sends session/cancel to the agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="236.64" height="17" as="geometry"/></mxCell>
179+ <mxCell id="02-conversation-78" value="answering in this channel (ADR 0001)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="257.52" height="17" as="geometry"/></mxCell>
180180 <mxCell id="02-conversation-79" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="94" as="geometry"/></mxCell>
181181 <mxCell id="02-conversation-80" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>
182- <mxCell id="02-conversation-81" value="Jauge de contexte" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>
183- <mxCell id="02-conversation-82" value="usage_update : prévient avant que la" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>
184- <mxCell id="02-conversation-83" value="conversation ne dépasse la mémoire de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="257.52" height="17" as="geometry"/></mxCell>
185- <mxCell id="02-conversation-84" value="l'agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="532" width="55.67999999999999" height="17" as="geometry"/></mxCell>
182+ <mxCell id="02-conversation-81" value="Context gauge" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
183+ <mxCell id="02-conversation-82" value="usage_update: warns before the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="208.79999999999998" height="17" as="geometry"/></mxCell>
184+ <mxCell id="02-conversation-83" value="conversation outgrows the agent's" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="229.67999999999998" height="17" as="geometry"/></mxCell>
185+ <mxCell id="02-conversation-84" value="memory." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="532" width="48.72" height="17" as="geometry"/></mxCell>
186186 </root>
187187 </mxGraphModel>
188188 </diagram>
189- <diagram id="03-mentions-commands" name="3 · Invoquer un agent (@) et une commande (/)">
189+ <diagram id="03-mentions-commands" name="3 · Calling an agent (@) and a command (/)">
190190 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
191191 <root>
192192 <mxCell id="0"/>
193193 <mxCell id="1" parent="0"/>
194- <mxCell id="03-mentions-commands-title" value="&lt;b&gt;3 · Invoquer un agent (@) et une commande (/)&lt;/b&gt;&lt;br&gt;&lt;i&gt;Tab choisit la suggestion ; continuer à écrire filtre la liste&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
194+ <mxCell id="03-mentions-commands-title" value="&lt;b&gt;3 · Calling an agent (@) and a command (/)&lt;/b&gt;&lt;br&gt;&lt;i&gt;Tab picks the suggestion; typing on filters the list&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
195195 <mxCell id="03-mentions-commands-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
196196 <mxCell id="03-mentions-commands-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
197197 <mxCell id="03-mentions-commands-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -200,14 +200,14 @@
200200 <mxCell id="03-mentions-commands-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
201201 <mxCell id="03-mentions-commands-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
202202 <mxCell id="03-mentions-commands-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
203- <mxCell id="03-mentions-commands-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>
203+ <mxCell id="03-mentions-commands-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
204204 <mxCell id="03-mentions-commands-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
205205 <mxCell id="03-mentions-commands-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
206206 <mxCell id="03-mentions-commands-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
207207 <mxCell id="03-mentions-commands-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
208208 <mxCell id="03-mentions-commands-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
209- <mxCell id="03-mentions-commands-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
210- <mxCell id="03-mentions-commands-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
209+ <mxCell id="03-mentions-commands-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
210+ <mxCell id="03-mentions-commands-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
211211 <mxCell id="03-mentions-commands-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
212212 <mxCell id="03-mentions-commands-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
213213 <mxCell id="03-mentions-commands-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -218,36 +218,36 @@
218218 <mxCell id="03-mentions-commands-24" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="352.5" width="70.47" height="18.5" as="geometry"/></mxCell>
219219 <mxCell id="03-mentions-commands-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
220220 <mxCell id="03-mentions-commands-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
221- <mxCell id="03-mentions-commands-27" value="Mention d'un agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="136" width="146.16" height="19" as="geometry"/></mxCell>
221+ <mxCell id="03-mentions-commands-27" value="Mentioning an agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="136" width="154.28" height="19" as="geometry"/></mxCell>
222222 <mxCell id="03-mentions-commands-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="164" width="420" height="150" as="geometry"/></mxCell>
223223 <mxCell id="03-mentions-commands-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="172" width="408" height="40" as="geometry"/></mxCell>
224224 <mxCell id="03-mentions-commands-30" value="🐮 garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="176.5" width="78.3" height="18.5" as="geometry"/></mxCell>
225- <mxCell id="03-mentions-commands-31" value="Garfield (traducteur) — traduit FR ⇄ EN" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="194.5" width="260.13" height="16.5" as="geometry"/></mxCell>
225+ <mxCell id="03-mentions-commands-31" value="Garfield (translator) — translates FR ⇄ EN" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="194.5" width="280.14" height="16.5" as="geometry"/></mxCell>
226226 <mxCell id="03-mentions-commands-32" value="🧑‍🚀 riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="220.5" width="70.47" height="18.5" as="geometry"/></mxCell>
227- <mxCell id="03-mentions-commands-33" value="Riker (outils) — commandes et fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="238.5" width="253.45999999999998" height="16.5" as="geometry"/></mxCell>
227+ <mxCell id="03-mentions-commands-33" value="Riker (tools) — commands and files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="238.5" width="226.77999999999997" height="16.5" as="geometry"/></mxCell>
228228 <mxCell id="03-mentions-commands-34" value="👩 alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="264.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
229- <mxCell id="03-mentions-commands-35" value="Alice (assistante) — questions simples" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="282.5" width="253.45999999999998" height="16.5" as="geometry"/></mxCell>
229+ <mxCell id="03-mentions-commands-35" value="Alice (assistant) — simple questions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="282.5" width="240.11999999999998" height="16.5" as="geometry"/></mxCell>
230230 <mxCell id="03-mentions-commands-36" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="324" width="969" height="64" as="geometry"/></mxCell>
231231 <mxCell id="03-mentions-commands-37" value="@ga" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="336" width="24.36" height="19" as="geometry"/></mxCell>
232- <mxCell id="03-mentions-commands-38" value="Tab : @garfield · ↑↓ naviguer · Échap fermer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="363" width="280.71999999999997" height="16" as="geometry"/></mxCell>
233- <mxCell id="03-mentions-commands-39" value="Commande d'un agent (plusieurs agents dans le channel)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="426" width="438.47999999999996" height="19" as="geometry"/></mxCell>
232+ <mxCell id="03-mentions-commands-38" value="Tab: @garfield · ↑↓ move · Esc close" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="363" width="229.67999999999998" height="16" as="geometry"/></mxCell>
233+ <mxCell id="03-mentions-commands-39" value="An agent's command (several agents in the channel)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="426" width="406.0" height="19" as="geometry"/></mxCell>
234234 <mxCell id="03-mentions-commands-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="454" width="520" height="196" as="geometry"/></mxCell>
235235 <mxCell id="03-mentions-commands-41" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="462" width="508" height="42" as="geometry"/></mxCell>
236236 <mxCell id="03-mentions-commands-42" value="/garfield:idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="467.5" width="133.92" height="18.5" as="geometry"/></mxCell>
237- <mxCell id="03-mentions-commands-43" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="469.5" width="49.91" height="16.5" as="geometry"/></mxCell>
238- <mxCell id="03-mentions-commands-44" value="Expressions idiomatiques et faux amis" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="486.5" width="246.79" height="16.5" as="geometry"/></mxCell>
237+ <mxCell id="03-mentions-commands-43" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="469.5" width="42.78" height="16.5" as="geometry"/></mxCell>
238+ <mxCell id="03-mentions-commands-44" value="Idioms and false friends" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="486.5" width="160.07999999999998" height="16.5" as="geometry"/></mxCell>
239239 <mxCell id="03-mentions-commands-45" value="/garfield:styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="513.5" width="133.92" height="18.5" as="geometry"/></mxCell>
240- <mxCell id="03-mentions-commands-46" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="515.5" width="49.91" height="16.5" as="geometry"/></mxCell>
241- <mxCell id="03-mentions-commands-47" value="Plusieurs versions d'une traduction" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="532.5" width="233.45" height="16.5" as="geometry"/></mxCell>
240+ <mxCell id="03-mentions-commands-46" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="515.5" width="42.78" height="16.5" as="geometry"/></mxCell>
241+ <mxCell id="03-mentions-commands-47" value="Several versions of a translation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="532.5" width="220.10999999999999" height="16.5" as="geometry"/></mxCell>
242242 <mxCell id="03-mentions-commands-48" value="/garfield:email-letter" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="559.5" width="184.14" height="18.5" as="geometry"/></mxCell>
243- <mxCell id="03-mentions-commands-49" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="561.5" width="49.91" height="16.5" as="geometry"/></mxCell>
244- <mxCell id="03-mentions-commands-50" value="E-mails et lettres : formules, tu ou vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="578.5" width="273.46999999999997" height="16.5" as="geometry"/></mxCell>
243+ <mxCell id="03-mentions-commands-49" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="561.5" width="42.78" height="16.5" as="geometry"/></mxCell>
244+ <mxCell id="03-mentions-commands-50" value="E-mails and letters: greetings, tu or vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="578.5" width="280.14" height="16.5" as="geometry"/></mxCell>
245245 <mxCell id="03-mentions-commands-51" value="/alice:compact" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="605.5" width="117.17999999999999" height="18.5" as="geometry"/></mxCell>
246246 <mxCell id="03-mentions-commands-52" value="" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="751" y="607.5" width="20" height="16.5" as="geometry"/></mxCell>
247- <mxCell id="03-mentions-commands-53" value="Résumer l'historique maintenant" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="624.5" width="206.76999999999998" height="16.5" as="geometry"/></mxCell>
247+ <mxCell id="03-mentions-commands-53" value="Summarise the history now" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="624.5" width="166.75" height="16.5" as="geometry"/></mxCell>
248248 <mxCell id="03-mentions-commands-54" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="660" width="969" height="64" as="geometry"/></mxCell>
249249 <mxCell id="03-mentions-commands-55" value="/gar" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="672" width="32.48" height="19" as="geometry"/></mxCell>
250- <mxCell id="03-mentions-commands-56" value="Tab : /garfield:idioms · le texte après la commande est son argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="699" width="433.84" height="16" as="geometry"/></mxCell>
250+ <mxCell id="03-mentions-commands-56" value="Tab: /garfield:idioms · the text after the command is its argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="699" width="421.08" height="16" as="geometry"/></mxCell>
251251 <mxCell id="03-mentions-commands-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="693" y="168" width="24" height="24" as="geometry"/></mxCell>
252252 <mxCell id="03-mentions-commands-58" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="559" y="344" width="24" height="24" as="geometry"/></mxCell>
253253 <mxCell id="03-mentions-commands-59" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="793" y="458" width="24" height="24" as="geometry"/></mxCell>
@@ -255,38 +255,38 @@
255255 <mxCell id="03-mentions-commands-61" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1208" y="680" width="24" height="24" as="geometry"/></mxCell>
256256 <mxCell id="03-mentions-commands-62" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
257257 <mxCell id="03-mentions-commands-63" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
258- <mxCell id="03-mentions-commands-64" value="@ liste les agents de la config" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="242.73" height="18.5" as="geometry"/></mxCell>
259- <mxCell id="03-mentions-commands-65" value="Nom technique + titre + description." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="250.55999999999997" height="17" as="geometry"/></mxCell>
260- <mxCell id="03-mentions-commands-66" value="Filtrage flou sur nom et titre (« gar »)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="285.35999999999996" height="17" as="geometry"/></mxCell>
258+ <mxCell id="03-mentions-commands-64" value="@ lists the configured agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="227.07" height="18.5" as="geometry"/></mxCell>
259+ <mxCell id="03-mentions-commands-65" value="Technical name + title + description." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="257.52" height="17" as="geometry"/></mxCell>
260+ <mxCell id="03-mentions-commands-66" value="Fuzzy filter on name and title (&quot;gar&quot;)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="271.44" height="17" as="geometry"/></mxCell>
261261 <mxCell id="03-mentions-commands-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
262262 <mxCell id="03-mentions-commands-68" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
263- <mxCell id="03-mentions-commands-69" value="Tab sélectionne, sinon on écrit" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="242.73" height="18.5" as="geometry"/></mxCell>
264- <mxCell id="03-mentions-commands-70" value="Comportement demandé dans le draft (#1)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="278.4" height="17" as="geometry"/></mxCell>
265- <mxCell id="03-mentions-commands-71" value="Échap ferme sans rien insérer." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="208.79999999999998" height="17" as="geometry"/></mxCell>
263+ <mxCell id="03-mentions-commands-69" value="Tab picks, otherwise you type" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="227.07" height="18.5" as="geometry"/></mxCell>
264+ <mxCell id="03-mentions-commands-70" value="Behaviour asked for in the draft (#1)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="264.47999999999996" height="17" as="geometry"/></mxCell>
265+ <mxCell id="03-mentions-commands-71" value="Esc closes without inserting anything." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="264.47999999999996" height="17" as="geometry"/></mxCell>
266266 <mxCell id="03-mentions-commands-72" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
267267 <mxCell id="03-mentions-commands-73" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
268- <mxCell id="03-mentions-commands-74" value="/agent:commande en multi-agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="242.73" height="18.5" as="geometry"/></mxCell>
269- <mxCell id="03-mentions-commands-75" value="Avec un seul agent : /commande. Sidekicks" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="285.35999999999996" height="17" as="geometry"/></mxCell>
270- <mxCell id="03-mentions-commands-76" value="envoie « /idioms … » au seul Garfield." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>
268+ <mxCell id="03-mentions-commands-74" value="/agent:command with several agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="266.21999999999997" height="18.5" as="geometry"/></mxCell>
269+ <mxCell id="03-mentions-commands-75" value="With one agent: /command. Sidekicks" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="243.6" height="17" as="geometry"/></mxCell>
270+ <mxCell id="03-mentions-commands-76" value="sends &quot;/idioms …&quot; to Garfield only." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="243.6" height="17" as="geometry"/></mxCell>
271271 <mxCell id="03-mentions-commands-77" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
272272 <mxCell id="03-mentions-commands-78" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
273- <mxCell id="03-mentions-commands-79" value="Skills et commandes intégrées" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="227.07" height="18.5" as="geometry"/></mxCell>
274- <mxCell id="03-mentions-commands-80" value="mm annonce compact, new, unload et ses" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="264.47999999999996" height="17" as="geometry"/></mxCell>
275- <mxCell id="03-mentions-commands-81" value="skills (available_commands_update)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="243.6" height="17" as="geometry"/></mxCell>
273+ <mxCell id="03-mentions-commands-79" value="Skills and built-in commands" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="219.23999999999998" height="18.5" as="geometry"/></mxCell>
274+ <mxCell id="03-mentions-commands-80" value="mm announces compact, new, unload and" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="257.52" height="17" as="geometry"/></mxCell>
275+ <mxCell id="03-mentions-commands-81" value="its skills (available_commands_update)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="271.44" height="17" as="geometry"/></mxCell>
276276 <mxCell id="03-mentions-commands-82" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>
277277 <mxCell id="03-mentions-commands-83" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>
278- <mxCell id="03-mentions-commands-84" value="Indice d'argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>
279- <mxCell id="03-mentions-commands-85" value="input.hint de la commande ACP, affiché" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="264.47999999999996" height="17" as="geometry"/></mxCell>
280- <mxCell id="03-mentions-commands-86" value="tant que l'argument n'est pas saisi." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>
278+ <mxCell id="03-mentions-commands-84" value="Argument hint" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
279+ <mxCell id="03-mentions-commands-85" value="The ACP command's input.hint, shown" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="243.6" height="17" as="geometry"/></mxCell>
280+ <mxCell id="03-mentions-commands-86" value="until the argument is typed." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="194.88" height="17" as="geometry"/></mxCell>
281281 </root>
282282 </mxGraphModel>
283283 </diagram>
284- <diagram id="04-permission" name="4 · Demande de permission">
284+ <diagram id="04-permission" name="4 · Permission request">
285285 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
286286 <root>
287287 <mxCell id="0"/>
288288 <mxCell id="1" parent="0"/>
289- <mxCell id="04-permission-title" value="&lt;b&gt;4 · Demande de permission&lt;/b&gt;&lt;br&gt;&lt;i&gt;En langage clair, sur la conversation concernée, avec une sortie de secours&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
289+ <mxCell id="04-permission-title" value="&lt;b&gt;4 · Permission request&lt;/b&gt;&lt;br&gt;&lt;i&gt;In plain words, on the conversation concerned, with a way out&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
290290 <mxCell id="04-permission-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
291291 <mxCell id="04-permission-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
292292 <mxCell id="04-permission-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -295,13 +295,13 @@
295295 <mxCell id="04-permission-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
296296 <mxCell id="04-permission-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
297297 <mxCell id="04-permission-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
298- <mxCell id="04-permission-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>
298+ <mxCell id="04-permission-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
299299 <mxCell id="04-permission-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
300300 <mxCell id="04-permission-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
301301 <mxCell id="04-permission-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
302302 <mxCell id="04-permission-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
303- <mxCell id="04-permission-14" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
304- <mxCell id="04-permission-15" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
303+ <mxCell id="04-permission-14" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
304+ <mxCell id="04-permission-15" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
305305 <mxCell id="04-permission-16" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="218" width="214" height="26" as="geometry"/></mxCell>
306306 <mxCell id="04-permission-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
307307 <mxCell id="04-permission-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
@@ -315,28 +315,28 @@
315315 <mxCell id="04-permission-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
316316 <mxCell id="04-permission-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>
317317 <mxCell id="04-permission-28" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="52.199999999999996" height="20" as="geometry"/></mxCell>
318- <mxCell id="04-permission-29" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>
319- <mxCell id="04-permission-30" value="@riker supprime les fichiers temporaires du dossier" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="399.33" height="18.5" as="geometry"/></mxCell>
318+ <mxCell id="04-permission-29" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="23.49" height="18.5" as="geometry"/></mxCell>
319+ <mxCell id="04-permission-30" value="@riker delete the temporary files in the folder" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="368.01" height="18.5" as="geometry"/></mxCell>
320320 <mxCell id="04-permission-31" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="70.47" height="18.5" as="geometry"/></mxCell>
321- <mxCell id="04-permission-32" value="attend votre réponse" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#C98A1B;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="239.5" width="145.0" height="17.5" as="geometry"/></mxCell>
321+ <mxCell id="04-permission-32" value="waiting for your answer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#C98A1B;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="239.5" width="166.75" height="17.5" as="geometry"/></mxCell>
322322 <mxCell id="04-permission-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#C98A1B;strokeWidth=2;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="270" width="720" height="330" as="geometry"/></mxCell>
323- <mxCell id="04-permission-34" value="🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="288" width="528.9599999999999" height="21" as="geometry"/></mxCell>
323+ <mxCell id="04-permission-34" value="🧑‍🚀 Riker wants to run a command on your computer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="288" width="454.71999999999997" height="21" as="geometry"/></mxCell>
324324 <mxCell id="04-permission-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="322" width="680" height="40" as="geometry"/></mxCell>
325325 <mxCell id="04-permission-36" value="rm -f *.tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="309" y="333" width="95.48" height="19" as="geometry"/></mxCell>
326- <mxCell id="04-permission-37" value="Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="375" width="580.5799999999999" height="18" as="geometry"/></mxCell>
327- <mxCell id="04-permission-38" value="dans agents/riker . Ils ne passent pas par la corbeille." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="395" width="429.78" height="18" as="geometry"/></mxCell>
328- <mxCell id="04-permission-39" value="⚠ Action irréversible" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="425" width="165.88" height="18" as="geometry"/></mxCell>
326+ <mxCell id="04-permission-37" value="What it does: permanently deletes the files ending in .tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="375" width="437.32" height="18" as="geometry"/></mxCell>
327+ <mxCell id="04-permission-38" value="in agents/riker . They do not go to the trash." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="395" width="354.38" height="18" as="geometry"/></mxCell>
328+ <mxCell id="04-permission-39" value="⚠ Cannot be undone" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="425" width="143.26" height="18" as="geometry"/></mxCell>
329329 <mxCell id="04-permission-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="460" width="170" height="32" as="geometry"/></mxCell>
330- <mxCell id="04-permission-41" value="Autoriser une fois" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="312.14" y="468.0" width="135.72" height="18" as="geometry"/></mxCell>
330+ <mxCell id="04-permission-41" value="Allow once" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="342.3" y="468.0" width="75.39999999999999" height="18" as="geometry"/></mxCell>
331331 <mxCell id="04-permission-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="460" width="300" height="32" as="geometry"/></mxCell>
332- <mxCell id="04-permission-43" value="Toujours autoriser bash ici" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="523.21" y="468.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>
332+ <mxCell id="04-permission-43" value="Always allow bash here" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="542.06" y="468.0" width="165.88" height="18" as="geometry"/></mxCell>
333333 <mxCell id="04-permission-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="504" width="170" height="32" as="geometry"/></mxCell>
334- <mxCell id="04-permission-45" value="Refuser" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="353.61" y="512.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>
334+ <mxCell id="04-permission-45" value="Reject" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="357.38" y="512.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
335335 <mxCell id="04-permission-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="504" width="300" height="32" as="geometry"/></mxCell>
336- <mxCell id="04-permission-47" value="Ne plus jamais proposer bash ici" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="504.36" y="512.0" width="241.27999999999997" height="18" as="geometry"/></mxCell>
336+ <mxCell id="04-permission-47" value="Never offer bash here again" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="523.21" y="512.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>
337337 <mxCell id="04-permission-48" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="795" y="504" width="180" height="32" as="geometry"/></mxCell>
338- <mxCell id="04-permission-49" value="■ Arrêter Riker" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="828.45" y="512.0" width="113.1" height="18" as="geometry"/></mxCell>
339- <mxCell id="04-permission-50" value="▸ Détails techniques (tool call bash, rawInput)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="558" width="327.12" height="17" as="geometry"/></mxCell>
338+ <mxCell id="04-permission-49" value="■ Stop Riker" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="839.76" y="512.0" width="90.47999999999999" height="18" as="geometry"/></mxCell>
339+ <mxCell id="04-permission-50" value="▸ Technical details (bash tool call, rawInput)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="558" width="320.15999999999997" height="17" as="geometry"/></mxCell>
340340 <mxCell id="04-permission-51" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="967" y="288" width="24" height="24" as="geometry"/></mxCell>
341341 <mxCell id="04-permission-52" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="943" y="386" width="24" height="24" as="geometry"/></mxCell>
342342 <mxCell id="04-permission-53" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="775" y="464" width="24" height="24" as="geometry"/></mxCell>
@@ -344,39 +344,39 @@
344344 <mxCell id="04-permission-55" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="643" y="554" width="24" height="24" as="geometry"/></mxCell>
345345 <mxCell id="04-permission-56" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
346346 <mxCell id="04-permission-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
347- <mxCell id="04-permission-58" value="Attaché à la conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="195.75" height="18.5" as="geometry"/></mxCell>
348- <mxCell id="04-permission-59" value="Bloque seulement la session de Riker ; les" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>
349- <mxCell id="04-permission-60" value="autres channels continuent (#17)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="229.67999999999998" height="17" as="geometry"/></mxCell>
347+ <mxCell id="04-permission-58" value="Attached to the conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="219.23999999999998" height="18.5" as="geometry"/></mxCell>
348+ <mxCell id="04-permission-59" value="Blocks only Riker's session; the other" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="264.47999999999996" height="17" as="geometry"/></mxCell>
349+ <mxCell id="04-permission-60" value="channels go on (#17)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="146.16" height="17" as="geometry"/></mxCell>
350350 <mxCell id="04-permission-61" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="94" as="geometry"/></mxCell>
351351 <mxCell id="04-permission-62" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
352- <mxCell id="04-permission-63" value="Expliquer, pas montrer du JSON" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
353- <mxCell id="04-permission-64" value="Titre ACP + kind + rawInput traduits en" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="271.44" height="17" as="geometry"/></mxCell>
354- <mxCell id="04-permission-65" value="phrase. Les actions destructives sont" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="257.52" height="17" as="geometry"/></mxCell>
355- <mxCell id="04-permission-66" value="signalées." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="244" width="69.6" height="17" as="geometry"/></mxCell>
352+ <mxCell id="04-permission-63" value="Explain, do not show JSON" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="195.75" height="18.5" as="geometry"/></mxCell>
353+ <mxCell id="04-permission-64" value="ACP title + kind + rawInput turned into" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="271.44" height="17" as="geometry"/></mxCell>
354+ <mxCell id="04-permission-65" value="a sentence. Destructive actions are" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="243.6" height="17" as="geometry"/></mxCell>
355+ <mxCell id="04-permission-66" value="flagged." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="244" width="55.67999999999999" height="17" as="geometry"/></mxCell>
356356 <mxCell id="04-permission-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="278" width="356" height="94" as="geometry"/></mxCell>
357357 <mxCell id="04-permission-68" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="288" width="24" height="24" as="geometry"/></mxCell>
358- <mxCell id="04-permission-69" value="Les 4 options ACP, par ids" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="291.5" width="203.57999999999998" height="18.5" as="geometry"/></mxCell>
358+ <mxCell id="04-permission-69" value="The 4 ACP options, by id" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="291.5" width="187.92" height="18.5" as="geometry"/></mxCell>
359359 <mxCell id="04-permission-70" value="allow · allow_always · reject · never." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>
360- <mxCell id="04-permission-71" value="« Ne plus jamais » est mis en avant au" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="264.47999999999996" height="17" as="geometry"/></mxCell>
361- <mxCell id="04-permission-72" value="lieu de « Refuser une fois »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="352" width="201.83999999999997" height="17" as="geometry"/></mxCell>
360+ <mxCell id="04-permission-71" value="&quot;Never again&quot; is put forward rather" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="243.6" height="17" as="geometry"/></mxCell>
361+ <mxCell id="04-permission-72" value="than &quot;Reject once&quot;." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="352" width="132.23999999999998" height="17" as="geometry"/></mxCell>
362362 <mxCell id="04-permission-73" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="386" width="356" height="76" as="geometry"/></mxCell>
363363 <mxCell id="04-permission-74" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="396" width="24" height="24" as="geometry"/></mxCell>
364- <mxCell id="04-permission-75" value="Toujours une sortie de secours" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="399.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
365- <mxCell id="04-permission-76" value="Spike ACP : après un refus, un petit modèle" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="299.28" height="17" as="geometry"/></mxCell>
366- <mxCell id="04-permission-77" value="a réessayé 40 fois. Arrêter = session/cancel." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="442" width="313.2" height="17" as="geometry"/></mxCell>
364+ <mxCell id="04-permission-75" value="Always a way out" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="399.5" width="125.27999999999999" height="18.5" as="geometry"/></mxCell>
365+ <mxCell id="04-permission-76" value="ACP spike: after a rejection, a small model" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="299.28" height="17" as="geometry"/></mxCell>
366+ <mxCell id="04-permission-77" value="retried 40 times. Stop = session/cancel." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="442" width="278.4" height="17" as="geometry"/></mxCell>
367367 <mxCell id="04-permission-78" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="476" width="356" height="58" as="geometry"/></mxCell>
368368 <mxCell id="04-permission-79" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="486" width="24" height="24" as="geometry"/></mxCell>
369- <mxCell id="04-permission-80" value="Détails pour les curieux" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="489.5" width="187.92" height="18.5" as="geometry"/></mxCell>
370- <mxCell id="04-permission-81" value="Repliés par défaut." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="132.23999999999998" height="17" as="geometry"/></mxCell>
369+ <mxCell id="04-permission-80" value="Details for the curious" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="489.5" width="180.08999999999997" height="18.5" as="geometry"/></mxCell>
370+ <mxCell id="04-permission-81" value="Folded by default." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="125.27999999999999" height="17" as="geometry"/></mxCell>
371371 </root>
372372 </mxGraphModel>
373373 </diagram>
374- <diagram id="05-agents-panel" name="5 · Panel Agents : fichiers, éditeur, terminal">
374+ <diagram id="05-agents-panel" name="5 · Agents panel: files, editor, terminal">
375375 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
376376 <root>
377377 <mxCell id="0"/>
378378 <mxCell id="1" parent="0"/>
379- <mxCell id="05-agents-panel-title" value="&lt;b&gt;5 · Panel Agents : fichiers, éditeur, terminal&lt;/b&gt;&lt;br&gt;&lt;i&gt;Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
379+ <mxCell id="05-agents-panel-title" value="&lt;b&gt;5 · Agents panel: files, editor, terminal&lt;/b&gt;&lt;br&gt;&lt;i&gt;The agent's workspace, whether it runs locally, in a container or in an sbx sandbox&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
380380 <mxCell id="05-agents-panel-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
381381 <mxCell id="05-agents-panel-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
382382 <mxCell id="05-agents-panel-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -385,13 +385,13 @@
385385 <mxCell id="05-agents-panel-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
386386 <mxCell id="05-agents-panel-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
387387 <mxCell id="05-agents-panel-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
388- <mxCell id="05-agents-panel-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>
388+ <mxCell id="05-agents-panel-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
389389 <mxCell id="05-agents-panel-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
390390 <mxCell id="05-agents-panel-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
391391 <mxCell id="05-agents-panel-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
392392 <mxCell id="05-agents-panel-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
393- <mxCell id="05-agents-panel-14" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
394- <mxCell id="05-agents-panel-15" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
393+ <mxCell id="05-agents-panel-14" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
394+ <mxCell id="05-agents-panel-15" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
395395 <mxCell id="05-agents-panel-16" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
396396 <mxCell id="05-agents-panel-17" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
397397 <mxCell id="05-agents-panel-18" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -403,15 +403,15 @@
403403 <mxCell id="05-agents-panel-24" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
404404 <mxCell id="05-agents-panel-25" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
405405 <mxCell id="05-agents-panel-26" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="56" as="geometry"/></mxCell>
406- <mxCell id="05-agents-panel-27" value="🐮 Garfield (traducteur)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="126" width="213.44" height="21" as="geometry"/></mxCell>
407- <mxCell id="05-agents-panel-28" value="● occupé · local · agents/garfield · gemma-4-E4B" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#C98A1B;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="267" y="150" width="334.08" height="17" as="geometry"/></mxCell>
406+ <mxCell id="05-agents-panel-27" value="🐮 Garfield (translator)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="126" width="213.44" height="21" as="geometry"/></mxCell>
407+ <mxCell id="05-agents-panel-28" value="● busy · local · agents/garfield · gemma-4-E4B" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#C98A1B;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="267" y="150" width="320.15999999999997" height="17" as="geometry"/></mxCell>
408408 <mxCell id="05-agents-panel-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="128" width="110" height="32" as="geometry"/></mxCell>
409- <mxCell id="05-agents-panel-30" value="Redémarrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1027.3" y="136.0" width="75.39999999999999" height="18" as="geometry"/></mxCell>
409+ <mxCell id="05-agents-panel-30" value="Restart" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1038.61" y="136.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>
410410 <mxCell id="05-agents-panel-31" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="128" width="110" height="32" as="geometry"/></mxCell>
411- <mxCell id="05-agents-panel-32" value="Arrêter" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1158.61" y="136.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>
412- <mxCell id="05-agents-panel-33" value="Fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#3B6FE0;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="184.5" width="62.63999999999999" height="18.5" as="geometry"/></mxCell>
411+ <mxCell id="05-agents-panel-32" value="Stop" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1169.92" y="136.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
412+ <mxCell id="05-agents-panel-33" value="Files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#3B6FE0;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="184.5" width="39.15" height="18.5" as="geometry"/></mxCell>
413413 <mxCell id="05-agents-panel-34" value="Terminal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="375" y="184.5" width="62.63999999999999" height="18.5" as="geometry"/></mxCell>
414- <mxCell id="05-agents-panel-35" value="Journal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="475" y="184.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
414+ <mxCell id="05-agents-panel-35" value="Log" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="475" y="184.5" width="23.49" height="18.5" as="geometry"/></mxCell>
415415 <mxCell id="05-agents-panel-36" value="" style="rounded=0;fillColor=#3B6FE0;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="267" y="206" width="70" height="2" as="geometry"/></mxCell>
416416 <mxCell id="05-agents-panel-37" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="216" width="260" height="420" as="geometry"/></mxCell>
417417 <mxCell id="05-agents-panel-38" value="▾ garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="232.5" width="77.5" height="17.5" as="geometry"/></mxCell>
@@ -424,20 +424,20 @@
424424 <mxCell id="05-agents-panel-45" value=" ▸ idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="388.5" width="93.0" height="17.5" as="geometry"/></mxCell>
425425 <mxCell id="05-agents-panel-46" value=" ▸ styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="414.5" width="93.0" height="17.5" as="geometry"/></mxCell>
426426 <mxCell id="05-agents-panel-47" value=" ▸ technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="440.5" width="155.0" height="17.5" as="geometry"/></mxCell>
427- <mxCell id="05-agents-panel-48" value=" traduction.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="466.5" width="131.75" height="17.5" as="geometry"/></mxCell>
428- <mxCell id="05-agents-panel-49" value="+ Nouveau fichier" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3B6FE0;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="503.5" width="123.24999999999999" height="17.5" as="geometry"/></mxCell>
427+ <mxCell id="05-agents-panel-48" value=" translation.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="466.5" width="139.5" height="17.5" as="geometry"/></mxCell>
428+ <mxCell id="05-agents-panel-49" value="+ New file" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3B6FE0;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="503.5" width="72.5" height="17.5" as="geometry"/></mxCell>
429429 <mxCell id="05-agents-panel-50" value="" style="rounded=0;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="521" y="216" width="729" height="420" as="geometry"/></mxCell>
430430 <mxCell id="05-agents-panel-51" value="AGENTS.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="535" y="227" width="67.86" height="18" as="geometry"/></mxCell>
431- <mxCell id="05-agents-panel-52" value="# Style de traduction" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="257.5" width="162.75" height="17.5" as="geometry"/></mxCell>
431+ <mxCell id="05-agents-panel-52" value="# Translation style" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="257.5" width="147.25" height="17.5" as="geometry"/></mxCell>
432432 <mxCell id="05-agents-panel-53" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="279.5" width="20" height="17.5" as="geometry"/></mxCell>
433- <mxCell id="05-agents-panel-54" value="- Registre : celui du texte source." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="301.5" width="271.25" height="17.5" as="geometry"/></mxCell>
434- <mxCell id="05-agents-panel-55" value="- Typographie française : espaces insécables" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="323.5" width="341.0" height="17.5" as="geometry"/></mxCell>
435- <mxCell id="05-agents-panel-56" value=" avant ; : ! ? et entre « guillemets »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="345.5" width="310.0" height="17.5" as="geometry"/></mxCell>
436- <mxCell id="05-agents-panel-57" value="- Ne jamais traduire le code ni les noms propres." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="367.5" width="379.75" height="17.5" as="geometry"/></mxCell>
433+ <mxCell id="05-agents-panel-54" value="- Register: the one of the source text." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="301.5" width="302.25" height="17.5" as="geometry"/></mxCell>
434+ <mxCell id="05-agents-panel-55" value="- French typography: non-breaking spaces" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="323.5" width="310.0" height="17.5" as="geometry"/></mxCell>
435+ <mxCell id="05-agents-panel-56" value=" before ; : ! ? and inside « quotes »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="345.5" width="302.25" height="17.5" as="geometry"/></mxCell>
436+ <mxCell id="05-agents-panel-57" value="- Never translate code or proper names." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="367.5" width="302.25" height="17.5" as="geometry"/></mxCell>
437437 <mxCell id="05-agents-panel-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="590" width="110" height="32" as="geometry"/></mxCell>
438- <mxCell id="05-agents-panel-59" value="Enregistrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1023.53" y="598.0" width="82.94" height="18" as="geometry"/></mxCell>
438+ <mxCell id="05-agents-panel-59" value="Save" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1049.92" y="598.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
439439 <mxCell id="05-agents-panel-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="590" width="110" height="32" as="geometry"/></mxCell>
440- <mxCell id="05-agents-panel-61" value="Supprimer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1151.07" y="598.0" width="67.86" height="18" as="geometry"/></mxCell>
440+ <mxCell id="05-agents-panel-61" value="Delete" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1162.38" y="598.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
441441 <mxCell id="05-agents-panel-62" value="" style="rounded=0;fillColor=#111317;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="646" width="1009" height="188" as="geometry"/></mxCell>
442442 <mxCell id="05-agents-panel-63" value="garfield $ ls skills" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="659.5" width="155.0" height="17.5" as="geometry"/></mxCell>
443443 <mxCell id="05-agents-panel-64" value="email-letter idioms styles technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="679.5" width="341.0" height="17.5" as="geometry"/></mxCell>
@@ -449,38 +449,38 @@
449449 <mxCell id="05-agents-panel-70" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="569" y="186" width="24" height="24" as="geometry"/></mxCell>
450450 <mxCell id="05-agents-panel-71" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
451451 <mxCell id="05-agents-panel-72" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
452- <mxCell id="05-agents-panel-73" value="État et emplacement de l'agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
453- <mxCell id="05-agents-panel-74" value="Statut temps réel (#11), runtime (local," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="278.4" height="17" as="geometry"/></mxCell>
454- <mxCell id="05-agents-panel-75" value="container, sbx), dossier et modèle." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="243.6" height="17" as="geometry"/></mxCell>
452+ <mxCell id="05-agents-panel-73" value="The agent's state and location" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
453+ <mxCell id="05-agents-panel-74" value="Live status (#11), runtime (local," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="236.64" height="17" as="geometry"/></mxCell>
454+ <mxCell id="05-agents-panel-75" value="container, sbx), folder and model." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="236.64" height="17" as="geometry"/></mxCell>
455455 <mxCell id="05-agents-panel-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
456456 <mxCell id="05-agents-panel-77" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
457- <mxCell id="05-agents-panel-78" value="Arbre du workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="140.94" height="18.5" as="geometry"/></mxCell>
458- <mxCell id="05-agents-panel-79" value="Chargement paresseux, rafraîchi quand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="257.52" height="17" as="geometry"/></mxCell>
459- <mxCell id="05-agents-panel-80" value="l'agent modifie un fichier (#29)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="229.67999999999998" height="17" as="geometry"/></mxCell>
457+ <mxCell id="05-agents-panel-78" value="Workspace tree" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
458+ <mxCell id="05-agents-panel-79" value="Loaded lazily, refreshed when the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="229.67999999999998" height="17" as="geometry"/></mxCell>
459+ <mxCell id="05-agents-panel-80" value="agent changes a file (#29)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="187.92" height="17" as="geometry"/></mxCell>
460460 <mxCell id="05-agents-panel-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
461461 <mxCell id="05-agents-panel-82" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
462- <mxCell id="05-agents-panel-83" value="Éditer, supprimer avec confirmation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="274.04999999999995" height="18.5" as="geometry"/></mxCell>
463- <mxCell id="05-agents-panel-84" value="Même éditeur que Settings. Conflit signalé" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="292.32" height="17" as="geometry"/></mxCell>
464- <mxCell id="05-agents-panel-85" value="si l'agent modifie le fichier (#30)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="250.55999999999997" height="17" as="geometry"/></mxCell>
462+ <mxCell id="05-agents-panel-83" value="Edit, delete with confirmation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
463+ <mxCell id="05-agents-panel-84" value="Same editor as Settings. A conflict is" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="264.47999999999996" height="17" as="geometry"/></mxCell>
464+ <mxCell id="05-agents-panel-85" value="flagged if the agent changes the file (#30)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="306.23999999999995" height="17" as="geometry"/></mxCell>
465465 <mxCell id="05-agents-panel-86" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
466466 <mxCell id="05-agents-panel-87" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
467- <mxCell id="05-agents-panel-88" value="Terminal sur le workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="195.75" height="18.5" as="geometry"/></mxCell>
468- <mxCell id="05-agents-panel-89" value="PTY local, docker exec ou sbx selon le" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="264.47999999999996" height="17" as="geometry"/></mxCell>
469- <mxCell id="05-agents-panel-90" value="runtime (#28, #31). Replié par défaut." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="264.47999999999996" height="17" as="geometry"/></mxCell>
467+ <mxCell id="05-agents-panel-88" value="Terminal in the workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="195.75" height="18.5" as="geometry"/></mxCell>
468+ <mxCell id="05-agents-panel-89" value="Local PTY, docker exec or sbx depending on" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="292.32" height="17" as="geometry"/></mxCell>
469+ <mxCell id="05-agents-panel-90" value="the runtime (#28, #31). Folded by default." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="292.32" height="17" as="geometry"/></mxCell>
470470 <mxCell id="05-agents-panel-91" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>
471471 <mxCell id="05-agents-panel-92" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>
472- <mxCell id="05-agents-panel-93" value="Journal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
473- <mxCell id="05-agents-panel-94" value="stderr de l'agent, pour comprendre" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="236.64" height="17" as="geometry"/></mxCell>
474- <mxCell id="05-agents-panel-95" value="une erreur sans ouvrir un terminal." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="243.6" height="17" as="geometry"/></mxCell>
472+ <mxCell id="05-agents-panel-93" value="Log" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="23.49" height="18.5" as="geometry"/></mxCell>
473+ <mxCell id="05-agents-panel-94" value="The agent's stderr, to understand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="229.67999999999998" height="17" as="geometry"/></mxCell>
474+ <mxCell id="05-agents-panel-95" value="an error without opening a terminal." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>
475475 </root>
476476 </mxGraphModel>
477477 </diagram>
478- <diagram id="06-first-launch" name="6 · Premier lancement">
478+ <diagram id="06-first-launch" name="6 · First launch">
479479 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
480480 <root>
481481 <mxCell id="0"/>
482482 <mxCell id="1" parent="0"/>
483- <mxCell id="06-first-launch-title" value="&lt;b&gt;6 · Premier lancement&lt;/b&gt;&lt;br&gt;&lt;i&gt;Moins d'une minute pour savoir quoi faire, sans lire de documentation&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
483+ <mxCell id="06-first-launch-title" value="&lt;b&gt;6 · First launch&lt;/b&gt;&lt;br&gt;&lt;i&gt;Less than a minute to know what to do, without reading any documentation&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
484484 <mxCell id="06-first-launch-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
485485 <mxCell id="06-first-launch-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
486486 <mxCell id="06-first-launch-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -489,57 +489,57 @@
489489 <mxCell id="06-first-launch-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
490490 <mxCell id="06-first-launch-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
491491 <mxCell id="06-first-launch-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
492- <mxCell id="06-first-launch-9" value="○ aucun agent configuré · config : créée à l'emplacement par défaut" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="446.89" height="16.5" as="geometry"/></mxCell>
492+ <mxCell id="06-first-launch-9" value="○ no agent configured · config: created at the default location" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="420.21" height="16.5" as="geometry"/></mxCell>
493493 <mxCell id="06-first-launch-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
494- <mxCell id="06-first-launch-11" value="👋 Bienvenue dans Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=26;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="443.96000000000004" y="174" width="392.08" height="31" as="geometry"/></mxCell>
495- <mxCell id="06-first-launch-12" value="Discutez avec des agents d'IA dans des channels, comme avec des collègues." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="318.1" y="219" width="643.8" height="20" as="geometry"/></mxCell>
494+ <mxCell id="06-first-launch-11" value="👋 Welcome to Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=26;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="474.12" y="174" width="331.76" height="31" as="geometry"/></mxCell>
495+ <mxCell id="06-first-launch-12" value="Talk with AI agents in channels, as you would with colleagues." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="370.3" y="219" width="539.4" height="20" as="geometry"/></mxCell>
496496 <mxCell id="06-first-launch-13" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="280" width="800" height="76" as="geometry"/></mxCell>
497497 <mxCell id="06-first-launch-14" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="300" width="36" height="36" as="geometry"/></mxCell>
498498 <mxCell id="06-first-launch-15" value="1" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="308" width="20" height="21" as="geometry"/></mxCell>
499- <mxCell id="06-first-launch-16" value="Ajoutez un agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="296" width="148.48" height="21" as="geometry"/></mxCell>
500- <mxCell id="06-first-launch-17" value="Un assistant qui tourne sur votre ordinateur, par exemple avec mm." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="322.5" width="516.78" height="18.5" as="geometry"/></mxCell>
499+ <mxCell id="06-first-launch-16" value="Add an agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="296" width="111.35999999999999" height="21" as="geometry"/></mxCell>
500+ <mxCell id="06-first-launch-17" value="An assistant that runs on your computer, for example with mm." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="322.5" width="477.63" height="18.5" as="geometry"/></mxCell>
501501 <mxCell id="06-first-launch-18" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="372" width="800" height="76" as="geometry"/></mxCell>
502502 <mxCell id="06-first-launch-19" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="392" width="36" height="36" as="geometry"/></mxCell>
503503 <mxCell id="06-first-launch-20" value="2" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="400" width="20" height="21" as="geometry"/></mxCell>
504- <mxCell id="06-first-launch-21" value="Ouvrez un channel" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="388" width="157.76" height="21" as="geometry"/></mxCell>
505- <mxCell id="06-first-launch-22" value="#général est déjà prêt. Créez-en d'autres par sujet." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="414.5" width="407.15999999999997" height="18.5" as="geometry"/></mxCell>
504+ <mxCell id="06-first-launch-21" value="Open a channel" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="388" width="129.92" height="21" as="geometry"/></mxCell>
505+ <mxCell id="06-first-launch-22" value="#general is ready. Create others, one per topic." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="414.5" width="375.84" height="18.5" as="geometry"/></mxCell>
506506 <mxCell id="06-first-launch-23" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="464" width="800" height="76" as="geometry"/></mxCell>
507507 <mxCell id="06-first-launch-24" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="484" width="36" height="36" as="geometry"/></mxCell>
508508 <mxCell id="06-first-launch-25" value="3" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="492" width="20" height="21" as="geometry"/></mxCell>
509- <mxCell id="06-first-launch-26" value="Appelez-le avec @" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="480" width="157.76" height="21" as="geometry"/></mxCell>
510- <mxCell id="06-first-launch-27" value="« @alice bonjour » : Alice rejoint le channel et vous répond." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="506.5" width="477.63" height="18.5" as="geometry"/></mxCell>
509+ <mxCell id="06-first-launch-26" value="Call it with @" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="480" width="129.92" height="21" as="geometry"/></mxCell>
510+ <mxCell id="06-first-launch-27" value="&quot;@alice hello&quot;: Alice joins the channel and answers you." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="506.5" width="438.47999999999996" height="18.5" as="geometry"/></mxCell>
511511 <mxCell id="06-first-launch-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="420" y="570" width="240" height="40" as="geometry"/></mxCell>
512- <mxCell id="06-first-launch-29" value="+ Ajouter mon premier agent" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="438.21000000000004" y="582.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>
512+ <mxCell id="06-first-launch-29" value="+ Add my first agent" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="464.6" y="582.0" width="150.79999999999998" height="18" as="geometry"/></mxCell>
513513 <mxCell id="06-first-launch-30" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="680" y="570" width="200" height="40" as="geometry"/></mxCell>
514- <mxCell id="06-first-launch-31" value="Ouvrir une configuration…" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="685.75" y="582.0" width="188.5" height="18" as="geometry"/></mxCell>
515- <mxCell id="06-first-launch-32" value="Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="305.92" y="638" width="668.16" height="17" as="geometry"/></mxCell>
514+ <mxCell id="06-first-launch-31" value="Open a configuration…" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="700.83" y="582.0" width="158.33999999999997" height="18" as="geometry"/></mxCell>
515+ <mxCell id="06-first-launch-32" value="Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295.48" y="638" width="689.04" height="17" as="geometry"/></mxCell>
516516 <mxCell id="06-first-launch-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="680" width="800" height="70" as="geometry"/></mxCell>
517- <mxCell id="06-first-launch-34" value="La commande « mm » est introuvable." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="260" y="694" width="284.2" height="19" as="geometry"/></mxCell>
518- <mxCell id="06-first-launch-35" value="Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="260" y="719" width="618.28" height="18" as="geometry"/></mxCell>
517+ <mxCell id="06-first-launch-34" value="The command &quot;mm&quot; cannot be found." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="260" y="694" width="267.96" height="19" as="geometry"/></mxCell>
518+ <mxCell id="06-first-launch-35" value="Install it (link to the guide) or choose another program for this agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="260" y="719" width="542.88" height="18" as="geometry"/></mxCell>
519519 <mxCell id="06-first-launch-36" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="300" width="24" height="24" as="geometry"/></mxCell>
520520 <mxCell id="06-first-launch-37" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="880" y="578" width="24" height="24" as="geometry"/></mxCell>
521521 <mxCell id="06-first-launch-38" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1018.0" y="634" width="24" height="24" as="geometry"/></mxCell>
522522 <mxCell id="06-first-launch-39" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="688" width="24" height="24" as="geometry"/></mxCell>
523523 <mxCell id="06-first-launch-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
524524 <mxCell id="06-first-launch-41" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
525- <mxCell id="06-first-launch-42" value="Trois étapes, pas plus" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="172.26" height="18.5" as="geometry"/></mxCell>
526- <mxCell id="06-first-launch-43" value="L'état vide explique le modèle mental :" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="271.44" height="17" as="geometry"/></mxCell>
525+ <mxCell id="06-first-launch-42" value="Three steps, no more" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="156.6" height="18.5" as="geometry"/></mxCell>
526+ <mxCell id="06-first-launch-43" value="The empty state explains the mental model:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>
527527 <mxCell id="06-first-launch-44" value="agents, channels, @ (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="180.95999999999998" height="17" as="geometry"/></mxCell>
528528 <mxCell id="06-first-launch-45" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
529529 <mxCell id="06-first-launch-46" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
530- <mxCell id="06-first-launch-47" value="Un assistant plutôt que du YAML" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="242.73" height="18.5" as="geometry"/></mxCell>
531- <mxCell id="06-first-launch-48" value="Le formulaire écrit le YAML à la place de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="285.35999999999996" height="17" as="geometry"/></mxCell>
532- <mxCell id="06-first-launch-49" value="l'utilisateur ; Settings reste accessible." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="292.32" height="17" as="geometry"/></mxCell>
530+ <mxCell id="06-first-launch-47" value="An assistant rather than YAML" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="227.07" height="18.5" as="geometry"/></mxCell>
531+ <mxCell id="06-first-launch-48" value="The form writes the YAML for the user;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="264.47999999999996" height="17" as="geometry"/></mxCell>
532+ <mxCell id="06-first-launch-49" value="Settings stays at hand." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="160.07999999999998" height="17" as="geometry"/></mxCell>
533533 <mxCell id="06-first-launch-50" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
534534 <mxCell id="06-first-launch-51" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
535- <mxCell id="06-first-launch-52" value="Rien à créer à la main" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="172.26" height="18.5" as="geometry"/></mxCell>
536- <mxCell id="06-first-launch-53" value="Le fichier par défaut existe déjà, avec des" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="299.28" height="17" as="geometry"/></mxCell>
537- <mxCell id="06-first-launch-54" value="commentaires (#9)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="125.27999999999999" height="17" as="geometry"/></mxCell>
535+ <mxCell id="06-first-launch-52" value="Nothing to create by hand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="195.75" height="18.5" as="geometry"/></mxCell>
536+ <mxCell id="06-first-launch-53" value="The default file already exists, with" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="257.52" height="17" as="geometry"/></mxCell>
537+ <mxCell id="06-first-launch-54" value="comments (#9)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="97.44" height="17" as="geometry"/></mxCell>
538538 <mxCell id="06-first-launch-55" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
539539 <mxCell id="06-first-launch-56" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
540- <mxCell id="06-first-launch-57" value="Erreurs humaines" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="125.27999999999999" height="18.5" as="geometry"/></mxCell>
541- <mxCell id="06-first-launch-58" value="Dire ce qui ne va pas et quoi faire ;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="257.52" height="17" as="geometry"/></mxCell>
542- <mxCell id="06-first-launch-59" value="jamais de trace technique (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="222.71999999999997" height="17" as="geometry"/></mxCell>
540+ <mxCell id="06-first-launch-57" value="Human errors" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="93.96" height="18.5" as="geometry"/></mxCell>
541+ <mxCell id="06-first-launch-58" value="Say what is wrong and what to do;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="229.67999999999998" height="17" as="geometry"/></mxCell>
542+ <mxCell id="06-first-launch-59" value="never a technical trace (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="208.79999999999998" height="17" as="geometry"/></mxCell>
543543 </root>
544544 </mxGraphModel>
545545 </diagram>
@@ -1,10 +1,10 @@
1 <mxfile host="app.diagrams.net">1 <mxfile host="app.diagrams.net">
2- <diagram id="01-layout" name="1 · Layout global">2+ <diagram id="01-layout" name="1 · Overall layout">
3 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">3 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
4 <root>4 <root>
5 <mxCell id="0"/>5 <mxCell id="0"/>
6 <mxCell id="1" parent="0"/>6 <mxCell id="1" parent="0"/>
7- <mxCell id="01-layout-title" value="&lt;b&gt;1 · Layout global&lt;/b&gt;&lt;br&gt;&lt;i&gt;Docks façon Zed, mais seul l'essentiel est visible par défaut&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>7+ <mxCell id="01-layout-title" value="&lt;b&gt;1 · Overall layout&lt;/b&gt;&lt;br&gt;&lt;i&gt;Zed-like docks, but only the essentials are visible by default&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
8 <mxCell id="01-layout-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>8 <mxCell id="01-layout-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
9 <mxCell id="01-layout-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>9 <mxCell id="01-layout-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
10 <mxCell id="01-layout-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>10 <mxCell id="01-layout-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -13,14 +13,14 @@
13 <mxCell id="01-layout-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>13 <mxCell id="01-layout-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
14 <mxCell id="01-layout-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>14 <mxCell id="01-layout-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
15 <mxCell id="01-layout-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>15 <mxCell id="01-layout-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
16- <mxCell id="01-layout-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>16+ <mxCell id="01-layout-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
17 <mxCell id="01-layout-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>17 <mxCell id="01-layout-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
18 <mxCell id="01-layout-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>18 <mxCell id="01-layout-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
19 <mxCell id="01-layout-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>19 <mxCell id="01-layout-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
20 <mxCell id="01-layout-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>20 <mxCell id="01-layout-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
21 <mxCell id="01-layout-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>21 <mxCell id="01-layout-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
22- <mxCell id="01-layout-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>22+ <mxCell id="01-layout-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
23- <mxCell id="01-layout-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>23+ <mxCell id="01-layout-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
24 <mxCell id="01-layout-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>24 <mxCell id="01-layout-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
25 <mxCell id="01-layout-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>25 <mxCell id="01-layout-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
26 <mxCell id="01-layout-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>26 <mxCell id="01-layout-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -32,18 +32,18 @@
32 <mxCell id="01-layout-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>32 <mxCell id="01-layout-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
33 <mxCell id="01-layout-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>33 <mxCell id="01-layout-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
34 <mxCell id="01-layout-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="709" height="44" as="geometry"/></mxCell>34 <mxCell id="01-layout-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="709" height="44" as="geometry"/></mxCell>
35- <mxCell id="01-layout-28" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>35+ <mxCell id="01-layout-28" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
36 <mxCell id="01-layout-29" value="👩 Alice · 🧔 Bob" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="830.9" y="131" width="113.1" height="18" as="geometry"/></mxCell>36 <mxCell id="01-layout-29" value="👩 Alice · 🧔 Bob" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="830.9" y="131" width="113.1" height="18" as="geometry"/></mxCell>
37- <mxCell id="01-layout-30" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>37+ <mxCell id="01-layout-30" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="23.49" height="18.5" as="geometry"/></mxCell>
38- <mxCell id="01-layout-31" value="@alice résume-moi ce que fait ACP en deux phrases" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="383.66999999999996" height="18.5" as="geometry"/></mxCell>38+ <mxCell id="01-layout-31" value="@alice sum up what ACP does in two sentences" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="344.52" height="18.5" as="geometry"/></mxCell>
39 <mxCell id="01-layout-32" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>39 <mxCell id="01-layout-32" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
40- <mxCell id="01-layout-33" value="ACP est un protocole qui permet à une application de dialoguer avec" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="260.5" width="524.61" height="18.5" as="geometry"/></mxCell>40+ <mxCell id="01-layout-33" value="ACP is a protocol that lets an application talk with any AI agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="260.5" width="516.78" height="18.5" as="geometry"/></mxCell>
41- <mxCell id="01-layout-34" value="n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="280.0" width="548.0999999999999" height="18.5" as="geometry"/></mxCell>41+ <mxCell id="01-layout-34" value="Sidekicks uses it to stay agent-agnostic." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="280.0" width="321.03" height="18.5" as="geometry"/></mxCell>
42 <mxCell id="01-layout-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="724" width="669" height="64" as="geometry"/></mxCell>42 <mxCell id="01-layout-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="724" width="669" height="64" as="geometry"/></mxCell>
43- <mxCell id="01-layout-36" value="Écrivez un message… (@ pour un agent, / pour une commande)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="736" width="479.08" height="19" as="geometry"/></mxCell>43+ <mxCell id="01-layout-36" value="Write a message… (@ for an agent, / for a command)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="736" width="414.11999999999995" height="19" as="geometry"/></mxCell>
44- <mxCell id="01-layout-37" value="Entrée pour envoyer · Maj+Entrée nouvelle ligne" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="763" width="299.85999999999996" height="16" as="geometry"/></mxCell>44+ <mxCell id="01-layout-37" value="Enter to send · Shift+Enter for a new line" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="763" width="267.96" height="16" as="geometry"/></mxCell>
45 <mxCell id="01-layout-38" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="960" y="116" width="300" height="718" as="geometry"/></mxCell>45 <mxCell id="01-layout-38" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="960" y="116" width="300" height="718" as="geometry"/></mxCell>
46- <mxCell id="01-layout-39" value="⚙︎ Settings — equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="131" width="188.5" height="18" as="geometry"/></mxCell>46+ <mxCell id="01-layout-39" value="⚙︎ Settings — team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="131" width="173.42" height="18" as="geometry"/></mxCell>
47 <mxCell id="01-layout-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="160" width="280" height="300" as="geometry"/></mxCell>47 <mxCell id="01-layout-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="160" width="280" height="300" as="geometry"/></mxCell>
48 <mxCell id="01-layout-41" value="version: 1" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="172" width="74.4" height="17" as="geometry"/></mxCell>48 <mxCell id="01-layout-41" value="version: 1" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="172" width="74.4" height="17" as="geometry"/></mxCell>
49 <mxCell id="01-layout-42" value="agents:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="191" width="52.08" height="17" as="geometry"/></mxCell>49 <mxCell id="01-layout-42" value="agents:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="191" width="52.08" height="17" as="geometry"/></mxCell>
@@ -52,13 +52,13 @@
52 <mxCell id="01-layout-45" value=" command: mm" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="248" width="111.6" height="17" as="geometry"/></mxCell>52 <mxCell id="01-layout-45" value=" command: mm" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="248" width="111.6" height="17" as="geometry"/></mxCell>
53 <mxCell id="01-layout-46" value=" args: [-acp, ...]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="267" width="156.24" height="17" as="geometry"/></mxCell>53 <mxCell id="01-layout-46" value=" args: [-acp, ...]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="267" width="156.24" height="17" as="geometry"/></mxCell>
54 <mxCell id="01-layout-47" value="channels:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="286" width="66.96" height="17" as="geometry"/></mxCell>54 <mxCell id="01-layout-47" value="channels:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="286" width="66.96" height="17" as="geometry"/></mxCell>
55- <mxCell id="01-layout-48" value=" - name: général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="305" width="126.48" height="17" as="geometry"/></mxCell>55+ <mxCell id="01-layout-48" value=" - name: general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="305" width="126.48" height="17" as="geometry"/></mxCell>
56 <mxCell id="01-layout-49" value=" agents: [alice, bob]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="324" width="178.56" height="17" as="geometry"/></mxCell>56 <mxCell id="01-layout-49" value=" agents: [alice, bob]" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="980" y="324" width="178.56" height="17" as="geometry"/></mxCell>
57- <mxCell id="01-layout-50" value="✓ Configuration valide" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3E9B4F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="477.5" width="159.5" height="17.5" as="geometry"/></mxCell>57+ <mxCell id="01-layout-50" value="✓ Valid configuration" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3E9B4F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="976" y="477.5" width="152.25" height="17.5" as="geometry"/></mxCell>
58 <mxCell id="01-layout-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="508" width="120" height="32" as="geometry"/></mxCell>58 <mxCell id="01-layout-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="970" y="508" width="120" height="32" as="geometry"/></mxCell>
59- <mxCell id="01-layout-52" value="Enregistrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="988.53" y="516.0" width="82.94" height="18" as="geometry"/></mxCell>59+ <mxCell id="01-layout-52" value="Save" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1014.92" y="516.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
60 <mxCell id="01-layout-53" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="804" width="709" height="30" as="geometry"/></mxCell>60 <mxCell id="01-layout-53" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="804" width="709" height="30" as="geometry"/></mxCell>
61- <mxCell id="01-layout-54" value="▸ Terminal · Fichiers (repliés)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="812" width="229.67999999999998" height="17" as="geometry"/></mxCell>61+ <mxCell id="01-layout-54" value="▸ Terminal · Files (folded)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="812" width="201.83999999999997" height="17" as="geometry"/></mxCell>
62 <mxCell id="01-layout-55" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="208" y="130" width="24" height="24" as="geometry"/></mxCell>62 <mxCell id="01-layout-55" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="208" y="130" width="24" height="24" as="geometry"/></mxCell>
63 <mxCell id="01-layout-56" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="359" y="228" width="24" height="24" as="geometry"/></mxCell>63 <mxCell id="01-layout-56" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="359" y="228" width="24" height="24" as="geometry"/></mxCell>
64 <mxCell id="01-layout-57" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1218" y="128" width="24" height="24" as="geometry"/></mxCell>64 <mxCell id="01-layout-57" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1218" y="128" width="24" height="24" as="geometry"/></mxCell>
@@ -66,39 +66,39 @@
66 <mxCell id="01-layout-59" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="428" y="835" width="24" height="24" as="geometry"/></mxCell>66 <mxCell id="01-layout-59" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="428" y="835" width="24" height="24" as="geometry"/></mxCell>
67 <mxCell id="01-layout-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>67 <mxCell id="01-layout-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
68 <mxCell id="01-layout-61" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>68 <mxCell id="01-layout-61" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
69- <mxCell id="01-layout-62" value="Dock gauche : channels et agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="250.55999999999997" height="18.5" as="geometry"/></mxCell>69+ <mxCell id="01-layout-62" value="Left dock: channels and agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
70- <mxCell id="01-layout-63" value="Toujours visible. La pastille donne l'état de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="313.2" height="17" as="geometry"/></mxCell>70+ <mxCell id="01-layout-63" value="Always visible. The dot gives the agent's" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="285.35999999999996" height="17" as="geometry"/></mxCell>
71- <mxCell id="01-layout-64" value="l'agent : vert prêt, orange occupé, gris arrêté." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="334.08" height="17" as="geometry"/></mxCell>71+ <mxCell id="01-layout-64" value="state: green ready, orange busy, grey stopped." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="320.15999999999997" height="17" as="geometry"/></mxCell>
72 <mxCell id="01-layout-65" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>72 <mxCell id="01-layout-65" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
73 <mxCell id="01-layout-66" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>73 <mxCell id="01-layout-66" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
74- <mxCell id="01-layout-67" value="Centre : la conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="187.92" height="18.5" as="geometry"/></mxCell>74+ <mxCell id="01-layout-67" value="Centre: the conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="187.92" height="18.5" as="geometry"/></mxCell>
75- <mxCell id="01-layout-68" value="Le seul panel indispensable. Pleine largeur" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="299.28" height="17" as="geometry"/></mxCell>75+ <mxCell id="01-layout-68" value="The only panel you cannot do without." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="257.52" height="17" as="geometry"/></mxCell>
76- <mxCell id="01-layout-69" value="quand les docks sont repliés." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="201.83999999999997" height="17" as="geometry"/></mxCell>76+ <mxCell id="01-layout-69" value="Full width when the docks are folded." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="257.52" height="17" as="geometry"/></mxCell>
77 <mxCell id="01-layout-70" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>77 <mxCell id="01-layout-70" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>
78 <mxCell id="01-layout-71" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>78 <mxCell id="01-layout-71" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
79- <mxCell id="01-layout-72" value="Dock droit : Settings" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>79+ <mxCell id="01-layout-72" value="Right dock: Settings" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="156.6" height="18.5" as="geometry"/></mxCell>
80- <mxCell id="01-layout-73" value="Éditeur YAML (CodeMirror) avec validation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="285.35999999999996" height="17" as="geometry"/></mxCell>80+ <mxCell id="01-layout-73" value="YAML editor (CodeMirror) with live" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="236.64" height="17" as="geometry"/></mxCell>
81- <mxCell id="01-layout-74" value="en direct. Fermé par défaut ; s'ouvre" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="257.52" height="17" as="geometry"/></mxCell>81+ <mxCell id="01-layout-74" value="validation. Closed by default; opens" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="250.55999999999997" height="17" as="geometry"/></mxCell>
82- <mxCell id="01-layout-75" value="par ⚙︎ ou ⌘,." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="90.47999999999999" height="17" as="geometry"/></mxCell>82+ <mxCell id="01-layout-75" value="with ⚙︎ or ⌘,." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="97.44" height="17" as="geometry"/></mxCell>
83 <mxCell id="01-layout-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>83 <mxCell id="01-layout-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>
84 <mxCell id="01-layout-77" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>84 <mxCell id="01-layout-77" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>
85- <mxCell id="01-layout-78" value="Dock bas : terminal et fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="242.73" height="18.5" as="geometry"/></mxCell>85+ <mxCell id="01-layout-78" value="Bottom dock: terminal and files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="242.73" height="18.5" as="geometry"/></mxCell>
86- <mxCell id="01-layout-79" value="Replié par défaut : utile aux curieux," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="264.47999999999996" height="17" as="geometry"/></mxCell>86+ <mxCell id="01-layout-79" value="Folded by default: there for the curious," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="285.35999999999996" height="17" as="geometry"/></mxCell>
87- <mxCell id="01-layout-80" value="jamais imposé au citizen user." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="208.79999999999998" height="17" as="geometry"/></mxCell>87+ <mxCell id="01-layout-80" value="never forced on the citizen user." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="229.67999999999998" height="17" as="geometry"/></mxCell>
88 <mxCell id="01-layout-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="76" as="geometry"/></mxCell>88 <mxCell id="01-layout-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="76" as="geometry"/></mxCell>
89 <mxCell id="01-layout-82" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>89 <mxCell id="01-layout-82" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>
90- <mxCell id="01-layout-83" value="Barre d'état" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="93.96" height="18.5" as="geometry"/></mxCell>90+ <mxCell id="01-layout-83" value="Status bar" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="78.3" height="18.5" as="geometry"/></mxCell>
91- <mxCell id="01-layout-84" value="Rappelle la limite d'agents simultanés" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="264.47999999999996" height="17" as="geometry"/></mxCell>91+ <mxCell id="01-layout-84" value="Shows the limit of agents running at once" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="285.35999999999996" height="17" as="geometry"/></mxCell>
92- <mxCell id="01-layout-85" value="(mémoire) et la configuration ouverte." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="264.47999999999996" height="17" as="geometry"/></mxCell>92+ <mxCell id="01-layout-85" value="(memory) and the open configuration." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="250.55999999999997" height="17" as="geometry"/></mxCell>
93 </root>93 </root>
94 </mxGraphModel>94 </mxGraphModel>
95 </diagram>95 </diagram>
96- <diagram id="02-conversation" name="2 · Conversation multi-agents">96+ <diagram id="02-conversation" name="2 · Multi-agent conversation">
97 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">97 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
98 <root>98 <root>
99 <mxCell id="0"/>99 <mxCell id="0"/>
100 <mxCell id="1" parent="0"/>100 <mxCell id="1" parent="0"/>
101- <mxCell id="02-conversation-title" value="&lt;b&gt;2 · Conversation multi-agents&lt;/b&gt;&lt;br&gt;&lt;i&gt;Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>101+ <mxCell id="02-conversation-title" value="&lt;b&gt;2 · Multi-agent conversation&lt;/b&gt;&lt;br&gt;&lt;i&gt;Each agent has its colour; tools are folded; you can always stop&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
102 <mxCell id="02-conversation-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>102 <mxCell id="02-conversation-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
103 <mxCell id="02-conversation-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>103 <mxCell id="02-conversation-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
104 <mxCell id="02-conversation-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>104 <mxCell id="02-conversation-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -107,14 +107,14 @@
107 <mxCell id="02-conversation-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>107 <mxCell id="02-conversation-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
108 <mxCell id="02-conversation-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>108 <mxCell id="02-conversation-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
109 <mxCell id="02-conversation-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>109 <mxCell id="02-conversation-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
110- <mxCell id="02-conversation-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>110+ <mxCell id="02-conversation-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
111 <mxCell id="02-conversation-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>111 <mxCell id="02-conversation-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
112 <mxCell id="02-conversation-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>112 <mxCell id="02-conversation-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
113 <mxCell id="02-conversation-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>113 <mxCell id="02-conversation-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
114 <mxCell id="02-conversation-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>114 <mxCell id="02-conversation-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
115 <mxCell id="02-conversation-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>115 <mxCell id="02-conversation-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
116- <mxCell id="02-conversation-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>116+ <mxCell id="02-conversation-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
117- <mxCell id="02-conversation-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>117+ <mxCell id="02-conversation-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
118 <mxCell id="02-conversation-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>118 <mxCell id="02-conversation-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
119 <mxCell id="02-conversation-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>119 <mxCell id="02-conversation-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
120 <mxCell id="02-conversation-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>120 <mxCell id="02-conversation-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -126,31 +126,31 @@
126 <mxCell id="02-conversation-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>126 <mxCell id="02-conversation-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
127 <mxCell id="02-conversation-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>127 <mxCell id="02-conversation-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
128 <mxCell id="02-conversation-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>128 <mxCell id="02-conversation-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>
129- <mxCell id="02-conversation-28" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>129+ <mxCell id="02-conversation-28" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="78.3" height="20" as="geometry"/></mxCell>
130 <mxCell id="02-conversation-29" value="👩 Alice · 🧔 Bob · 🧑‍🚀 Riker" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1040.42" y="131" width="203.57999999999998" height="18" as="geometry"/></mxCell>130 <mxCell id="02-conversation-29" value="👩 Alice · 🧔 Bob · 🧑‍🚀 Riker" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1040.42" y="131" width="203.57999999999998" height="18" as="geometry"/></mxCell>
131- <mxCell id="02-conversation-30" value="— Riker a rejoint le channel —" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="275" y="183.5" width="217.49999999999997" height="17.5" as="geometry"/></mxCell>131+ <mxCell id="02-conversation-30" value="— Riker joined the channel —" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="275" y="183.5" width="203.0" height="17.5" as="geometry"/></mxCell>
132- <mxCell id="02-conversation-31" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="214.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>132+ <mxCell id="02-conversation-31" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="214.5" width="23.49" height="18.5" as="geometry"/></mxCell>
133- <mxCell id="02-conversation-32" value="@alice propose un nom pour un script qui sauvegarde mes photos" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="236.5" width="485.46" height="18.5" as="geometry"/></mxCell>133+ <mxCell id="02-conversation-32" value="@alice suggest a name for a script that backs up my photos" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="236.5" width="454.14" height="18.5" as="geometry"/></mxCell>
134 <mxCell id="02-conversation-33" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="270.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>134 <mxCell id="02-conversation-33" value="👩 Alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#C2185B;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="270.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
135- <mxCell id="02-conversation-34" value="Je propose « photo-backup.sh » : court, explicite, et l'extension dit que" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="292.5" width="571.5899999999999" height="18.5" as="geometry"/></mxCell>135+ <mxCell id="02-conversation-34" value="I suggest &quot;photo-backup.sh&quot;: short, clear, and the extension says" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="292.5" width="508.95" height="18.5" as="geometry"/></mxCell>
136- <mxCell id="02-conversation-35" value="c'est un script shell." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="312.0" width="172.26" height="18.5" as="geometry"/></mxCell>136+ <mxCell id="02-conversation-35" value="it is a shell script." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="312.0" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>
137- <mxCell id="02-conversation-36" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="346.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>137+ <mxCell id="02-conversation-36" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="346.5" width="23.49" height="18.5" as="geometry"/></mxCell>
138- <mxCell id="02-conversation-37" value="@bob tu valides ? @riker crée le fichier vide" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="368.5" width="352.34999999999997" height="18.5" as="geometry"/></mxCell>138+ <mxCell id="02-conversation-37" value="@bob do you agree? @riker create the empty file" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="368.5" width="368.01" height="18.5" as="geometry"/></mxCell>
139 <mxCell id="02-conversation-38" value="🧔 Bob" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#00838F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="402.5" width="39.15" height="18.5" as="geometry"/></mxCell>139 <mxCell id="02-conversation-38" value="🧔 Bob" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#00838F;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="402.5" width="39.15" height="18.5" as="geometry"/></mxCell>
140- <mxCell id="02-conversation-39" value="Bon choix. Alice a raison : un nom en minuscules, sans espace." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="424.5" width="485.46" height="18.5" as="geometry"/></mxCell>140+ <mxCell id="02-conversation-39" value="Good choice. Alice is right: a lowercase name, no spaces." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="424.5" width="446.30999999999995" height="18.5" as="geometry"/></mxCell>
141 <mxCell id="02-conversation-40" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="458.5" width="70.47" height="18.5" as="geometry"/></mxCell>141 <mxCell id="02-conversation-40" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="458.5" width="70.47" height="18.5" as="geometry"/></mxCell>
142- <mxCell id="02-conversation-41" value="travaille…" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="459.5" width="72.5" height="17.5" as="geometry"/></mxCell>142+ <mxCell id="02-conversation-41" value="working…" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#6B6F7A;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="459.5" width="57.99999999999999" height="17.5" as="geometry"/></mxCell>
143 <mxCell id="02-conversation-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="484" width="560" height="40" as="geometry"/></mxCell>143 <mxCell id="02-conversation-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="484" width="560" height="40" as="geometry"/></mxCell>
144- <mxCell id="02-conversation-43" value="▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="496.5" width="434.99999999999994" height="17.5" as="geometry"/></mxCell>144+ <mxCell id="02-conversation-43" value="▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="496.5" width="406.0" height="17.5" as="geometry"/></mxCell>
145 <mxCell id="02-conversation-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="532" width="560" height="40" as="geometry"/></mxCell>145 <mxCell id="02-conversation-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=16;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="532" width="560" height="40" as="geometry"/></mxCell>
146- <mxCell id="02-conversation-45" value="▸ 🛠 bash : ls -la … en cours" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="544.5" width="449.49999999999994" height="17.5" as="geometry"/></mxCell>146+ <mxCell id="02-conversation-45" value="▸ 🛠 bash: ls -la … running" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="544.5" width="434.99999999999994" height="17.5" as="geometry"/></mxCell>
147 <mxCell id="02-conversation-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="754" width="849" height="64" as="geometry"/></mxCell>147 <mxCell id="02-conversation-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="754" width="849" height="64" as="geometry"/></mxCell>
148 <mxCell id="02-conversation-47" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="766" width="20" height="19" as="geometry"/></mxCell>148 <mxCell id="02-conversation-47" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="766" width="20" height="19" as="geometry"/></mxCell>
149- <mxCell id="02-conversation-48" value="Entrée pour envoyer · Maj+Entrée nouvelle ligne" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="793" width="299.85999999999996" height="16" as="geometry"/></mxCell>149+ <mxCell id="02-conversation-48" value="Enter to send · Shift+Enter for a new line" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="793" width="267.96" height="16" as="geometry"/></mxCell>
150 <mxCell id="02-conversation-49" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1132" y="770" width="108" height="32" as="geometry"/></mxCell>150 <mxCell id="02-conversation-49" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1132" y="770" width="108" height="32" as="geometry"/></mxCell>
151- <mxCell id="02-conversation-50" value="■ Arrêter" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1152.07" y="778.0" width="67.86" height="18" as="geometry"/></mxCell>151+ <mxCell id="02-conversation-50" value="■ Stop" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1163.38" y="778.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
152 <mxCell id="02-conversation-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=30;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1040" y="176" width="200" height="30" as="geometry"/></mxCell>152 <mxCell id="02-conversation-51" value="" style="rounded=1;absoluteArcSize=1;arcSize=30;fillColor=#F0F1F3;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1040" y="176" width="200" height="30" as="geometry"/></mxCell>
153- <mxCell id="02-conversation-52" value="contexte Riker : 41 %" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1069.965" y="184.5" width="140.07" height="16.5" as="geometry"/></mxCell>153+ <mxCell id="02-conversation-52" value="Riker's context: 41%" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1073.3" y="184.5" width="133.39999999999998" height="16.5" as="geometry"/></mxCell>
154 <mxCell id="02-conversation-53" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="563" y="179" width="24" height="24" as="geometry"/></mxCell>154 <mxCell id="02-conversation-53" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="563" y="179" width="24" height="24" as="geometry"/></mxCell>
155 <mxCell id="02-conversation-54" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="343" y="262" width="24" height="24" as="geometry"/></mxCell>155 <mxCell id="02-conversation-54" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="343" y="262" width="24" height="24" as="geometry"/></mxCell>
156 <mxCell id="02-conversation-55" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="843" y="492" width="24" height="24" as="geometry"/></mxCell>156 <mxCell id="02-conversation-55" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="843" y="492" width="24" height="24" as="geometry"/></mxCell>
@@ -158,40 +158,40 @@
158 <mxCell id="02-conversation-57" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1016" y="179" width="24" height="24" as="geometry"/></mxCell>158 <mxCell id="02-conversation-57" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1016" y="179" width="24" height="24" as="geometry"/></mxCell>
159 <mxCell id="02-conversation-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>159 <mxCell id="02-conversation-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
160 <mxCell id="02-conversation-59" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>160 <mxCell id="02-conversation-59" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
161- <mxCell id="02-conversation-60" value="Arrivée visible" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="117.44999999999999" height="18.5" as="geometry"/></mxCell>161+ <mxCell id="02-conversation-60" value="A visible arrival" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>
162- <mxCell id="02-conversation-61" value="La 1re mention invite l'agent : un message" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>162+ <mxCell id="02-conversation-61" value="The first mention invites the agent: a system" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="313.2" height="17" as="geometry"/></mxCell>
163- <mxCell id="02-conversation-62" value="système le dit. On sait qui « écoute »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="271.44" height="17" as="geometry"/></mxCell>163+ <mxCell id="02-conversation-62" value="message says so. You know who is listening." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="299.28" height="17" as="geometry"/></mxCell>
164 <mxCell id="02-conversation-63" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>164 <mxCell id="02-conversation-63" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
165 <mxCell id="02-conversation-64" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>165 <mxCell id="02-conversation-64" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
166- <mxCell id="02-conversation-65" value="Une couleur par agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="164.42999999999998" height="18.5" as="geometry"/></mxCell>166+ <mxCell id="02-conversation-65" value="One colour per agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="156.6" height="18.5" as="geometry"/></mxCell>
167- <mxCell id="02-conversation-66" value="Avatar + nom colorés, réponses en Markdown" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="292.32" height="17" as="geometry"/></mxCell>167+ <mxCell id="02-conversation-66" value="Coloured avatar and name, answers in" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="250.55999999999997" height="17" as="geometry"/></mxCell>
168- <mxCell id="02-conversation-67" value="avec code coloré (ADR 0002)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="194.88" height="17" as="geometry"/></mxCell>168+ <mxCell id="02-conversation-67" value="Markdown with highlighted code (ADR 0002)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="292.32" height="17" as="geometry"/></mxCell>
169 <mxCell id="02-conversation-68" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>169 <mxCell id="02-conversation-68" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="94" as="geometry"/></mxCell>
170 <mxCell id="02-conversation-69" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>170 <mxCell id="02-conversation-69" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
171- <mxCell id="02-conversation-70" value="Outils repliés" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>171+ <mxCell id="02-conversation-70" value="Folded tools" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="93.96" height="18.5" as="geometry"/></mxCell>
172- <mxCell id="02-conversation-71" value="Une ligne par tool call : titre lisible et" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="292.32" height="17" as="geometry"/></mxCell>172+ <mxCell id="02-conversation-71" value="One line per tool call: a readable title" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="278.4" height="17" as="geometry"/></mxCell>
173- <mxCell id="02-conversation-72" value="statut. Déplier montre la commande, le" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>173+ <mxCell id="02-conversation-72" value="and its status. Unfolding shows the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="243.6" height="17" as="geometry"/></mxCell>
174- <mxCell id="02-conversation-73" value="diff ou la sortie." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="125.27999999999999" height="17" as="geometry"/></mxCell>174+ <mxCell id="02-conversation-73" value="command, the diff or the output." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="222.71999999999997" height="17" as="geometry"/></mxCell>
175 <mxCell id="02-conversation-74" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>175 <mxCell id="02-conversation-74" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="368" width="356" height="76" as="geometry"/></mxCell>
176 <mxCell id="02-conversation-75" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>176 <mxCell id="02-conversation-75" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="378" width="24" height="24" as="geometry"/></mxCell>
177- <mxCell id="02-conversation-76" value="Toujours un bouton Arrêter" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="203.57999999999998" height="18.5" as="geometry"/></mxCell>177+ <mxCell id="02-conversation-76" value="Always a Stop button" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="381.5" width="156.6" height="18.5" as="geometry"/></mxCell>
178- <mxCell id="02-conversation-77" value="Envoie session/cancel aux agents qui" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="250.55999999999997" height="17" as="geometry"/></mxCell>178+ <mxCell id="02-conversation-77" value="Sends session/cancel to the agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="236.64" height="17" as="geometry"/></mxCell>
179- <mxCell id="02-conversation-78" value="répondent dans ce channel (ADR 0001)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="257.52" height="17" as="geometry"/></mxCell>179+ <mxCell id="02-conversation-78" value="answering in this channel (ADR 0001)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="257.52" height="17" as="geometry"/></mxCell>
180 <mxCell id="02-conversation-79" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="94" as="geometry"/></mxCell>180 <mxCell id="02-conversation-79" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="458" width="356" height="94" as="geometry"/></mxCell>
181 <mxCell id="02-conversation-80" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>181 <mxCell id="02-conversation-80" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="468" width="24" height="24" as="geometry"/></mxCell>
182- <mxCell id="02-conversation-81" value="Jauge de contexte" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>182+ <mxCell id="02-conversation-81" value="Context gauge" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="471.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
183- <mxCell id="02-conversation-82" value="usage_update : prévient avant que la" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>183+ <mxCell id="02-conversation-82" value="usage_update: warns before the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="208.79999999999998" height="17" as="geometry"/></mxCell>
184- <mxCell id="02-conversation-83" value="conversation ne dépasse la mémoire de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="257.52" height="17" as="geometry"/></mxCell>184+ <mxCell id="02-conversation-83" value="conversation outgrows the agent's" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="229.67999999999998" height="17" as="geometry"/></mxCell>
185- <mxCell id="02-conversation-84" value="l'agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="532" width="55.67999999999999" height="17" as="geometry"/></mxCell>185+ <mxCell id="02-conversation-84" value="memory." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="532" width="48.72" height="17" as="geometry"/></mxCell>
186 </root>186 </root>
187 </mxGraphModel>187 </mxGraphModel>
188 </diagram>188 </diagram>
189- <diagram id="03-mentions-commands" name="3 · Invoquer un agent (@) et une commande (/)">189+ <diagram id="03-mentions-commands" name="3 · Calling an agent (@) and a command (/)">
190 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">190 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
191 <root>191 <root>
192 <mxCell id="0"/>192 <mxCell id="0"/>
193 <mxCell id="1" parent="0"/>193 <mxCell id="1" parent="0"/>
194- <mxCell id="03-mentions-commands-title" value="&lt;b&gt;3 · Invoquer un agent (@) et une commande (/)&lt;/b&gt;&lt;br&gt;&lt;i&gt;Tab choisit la suggestion ; continuer à écrire filtre la liste&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>194+ <mxCell id="03-mentions-commands-title" value="&lt;b&gt;3 · Calling an agent (@) and a command (/)&lt;/b&gt;&lt;br&gt;&lt;i&gt;Tab picks the suggestion; typing on filters the list&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
195 <mxCell id="03-mentions-commands-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>195 <mxCell id="03-mentions-commands-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
196 <mxCell id="03-mentions-commands-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>196 <mxCell id="03-mentions-commands-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
197 <mxCell id="03-mentions-commands-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>197 <mxCell id="03-mentions-commands-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -200,14 +200,14 @@
200 <mxCell id="03-mentions-commands-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>200 <mxCell id="03-mentions-commands-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
201 <mxCell id="03-mentions-commands-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>201 <mxCell id="03-mentions-commands-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
202 <mxCell id="03-mentions-commands-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>202 <mxCell id="03-mentions-commands-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
203- <mxCell id="03-mentions-commands-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>203+ <mxCell id="03-mentions-commands-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
204 <mxCell id="03-mentions-commands-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>204 <mxCell id="03-mentions-commands-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
205 <mxCell id="03-mentions-commands-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>205 <mxCell id="03-mentions-commands-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
206 <mxCell id="03-mentions-commands-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>206 <mxCell id="03-mentions-commands-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
207 <mxCell id="03-mentions-commands-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>207 <mxCell id="03-mentions-commands-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
208 <mxCell id="03-mentions-commands-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>208 <mxCell id="03-mentions-commands-14" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="158" width="214" height="26" as="geometry"/></mxCell>
209- <mxCell id="03-mentions-commands-15" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>209+ <mxCell id="03-mentions-commands-15" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
210- <mxCell id="03-mentions-commands-16" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>210+ <mxCell id="03-mentions-commands-16" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
211 <mxCell id="03-mentions-commands-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>211 <mxCell id="03-mentions-commands-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
212 <mxCell id="03-mentions-commands-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>212 <mxCell id="03-mentions-commands-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
213 <mxCell id="03-mentions-commands-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>213 <mxCell id="03-mentions-commands-19" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -218,36 +218,36 @@
218 <mxCell id="03-mentions-commands-24" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="352.5" width="70.47" height="18.5" as="geometry"/></mxCell>218 <mxCell id="03-mentions-commands-24" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="352.5" width="70.47" height="18.5" as="geometry"/></mxCell>
219 <mxCell id="03-mentions-commands-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>219 <mxCell id="03-mentions-commands-25" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
220 <mxCell id="03-mentions-commands-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>220 <mxCell id="03-mentions-commands-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
221- <mxCell id="03-mentions-commands-27" value="Mention d'un agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="136" width="146.16" height="19" as="geometry"/></mxCell>221+ <mxCell id="03-mentions-commands-27" value="Mentioning an agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="136" width="154.28" height="19" as="geometry"/></mxCell>
222 <mxCell id="03-mentions-commands-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="164" width="420" height="150" as="geometry"/></mxCell>222 <mxCell id="03-mentions-commands-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="164" width="420" height="150" as="geometry"/></mxCell>
223 <mxCell id="03-mentions-commands-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="172" width="408" height="40" as="geometry"/></mxCell>223 <mxCell id="03-mentions-commands-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="172" width="408" height="40" as="geometry"/></mxCell>
224 <mxCell id="03-mentions-commands-30" value="🐮 garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="176.5" width="78.3" height="18.5" as="geometry"/></mxCell>224 <mxCell id="03-mentions-commands-30" value="🐮 garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="176.5" width="78.3" height="18.5" as="geometry"/></mxCell>
225- <mxCell id="03-mentions-commands-31" value="Garfield (traducteur) — traduit FR ⇄ EN" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="194.5" width="260.13" height="16.5" as="geometry"/></mxCell>225+ <mxCell id="03-mentions-commands-31" value="Garfield (translator) — translates FR ⇄ EN" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="194.5" width="280.14" height="16.5" as="geometry"/></mxCell>
226 <mxCell id="03-mentions-commands-32" value="🧑‍🚀 riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="220.5" width="70.47" height="18.5" as="geometry"/></mxCell>226 <mxCell id="03-mentions-commands-32" value="🧑‍🚀 riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="220.5" width="70.47" height="18.5" as="geometry"/></mxCell>
227- <mxCell id="03-mentions-commands-33" value="Riker (outils) — commandes et fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="238.5" width="253.45999999999998" height="16.5" as="geometry"/></mxCell>227+ <mxCell id="03-mentions-commands-33" value="Riker (tools) — commands and files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="238.5" width="226.77999999999997" height="16.5" as="geometry"/></mxCell>
228 <mxCell id="03-mentions-commands-34" value="👩 alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="264.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>228 <mxCell id="03-mentions-commands-34" value="👩 alice" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="289" y="264.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>
229- <mxCell id="03-mentions-commands-35" value="Alice (assistante) — questions simples" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="282.5" width="253.45999999999998" height="16.5" as="geometry"/></mxCell>229+ <mxCell id="03-mentions-commands-35" value="Alice (assistant) — simple questions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="282.5" width="240.11999999999998" height="16.5" as="geometry"/></mxCell>
230 <mxCell id="03-mentions-commands-36" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="324" width="969" height="64" as="geometry"/></mxCell>230 <mxCell id="03-mentions-commands-36" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="324" width="969" height="64" as="geometry"/></mxCell>
231 <mxCell id="03-mentions-commands-37" value="@ga" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="336" width="24.36" height="19" as="geometry"/></mxCell>231 <mxCell id="03-mentions-commands-37" value="@ga" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="336" width="24.36" height="19" as="geometry"/></mxCell>
232- <mxCell id="03-mentions-commands-38" value="Tab : @garfield · ↑↓ naviguer · Échap fermer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="363" width="280.71999999999997" height="16" as="geometry"/></mxCell>232+ <mxCell id="03-mentions-commands-38" value="Tab: @garfield · ↑↓ move · Esc close" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="363" width="229.67999999999998" height="16" as="geometry"/></mxCell>
233- <mxCell id="03-mentions-commands-39" value="Commande d'un agent (plusieurs agents dans le channel)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="426" width="438.47999999999996" height="19" as="geometry"/></mxCell>233+ <mxCell id="03-mentions-commands-39" value="An agent's command (several agents in the channel)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="271" y="426" width="406.0" height="19" as="geometry"/></mxCell>
234 <mxCell id="03-mentions-commands-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="454" width="520" height="196" as="geometry"/></mxCell>234 <mxCell id="03-mentions-commands-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="454" width="520" height="196" as="geometry"/></mxCell>
235 <mxCell id="03-mentions-commands-41" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="462" width="508" height="42" as="geometry"/></mxCell>235 <mxCell id="03-mentions-commands-41" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#E8EFFD;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="277" y="462" width="508" height="42" as="geometry"/></mxCell>
236 <mxCell id="03-mentions-commands-42" value="/garfield:idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="467.5" width="133.92" height="18.5" as="geometry"/></mxCell>236 <mxCell id="03-mentions-commands-42" value="/garfield:idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="467.5" width="133.92" height="18.5" as="geometry"/></mxCell>
237- <mxCell id="03-mentions-commands-43" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="469.5" width="49.91" height="16.5" as="geometry"/></mxCell>237+ <mxCell id="03-mentions-commands-43" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="469.5" width="42.78" height="16.5" as="geometry"/></mxCell>
238- <mxCell id="03-mentions-commands-44" value="Expressions idiomatiques et faux amis" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="486.5" width="246.79" height="16.5" as="geometry"/></mxCell>238+ <mxCell id="03-mentions-commands-44" value="Idioms and false friends" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="486.5" width="160.07999999999998" height="16.5" as="geometry"/></mxCell>
239 <mxCell id="03-mentions-commands-45" value="/garfield:styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="513.5" width="133.92" height="18.5" as="geometry"/></mxCell>239 <mxCell id="03-mentions-commands-45" value="/garfield:styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="513.5" width="133.92" height="18.5" as="geometry"/></mxCell>
240- <mxCell id="03-mentions-commands-46" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="515.5" width="49.91" height="16.5" as="geometry"/></mxCell>240+ <mxCell id="03-mentions-commands-46" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="515.5" width="42.78" height="16.5" as="geometry"/></mxCell>
241- <mxCell id="03-mentions-commands-47" value="Plusieurs versions d'une traduction" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="532.5" width="233.45" height="16.5" as="geometry"/></mxCell>241+ <mxCell id="03-mentions-commands-47" value="Several versions of a translation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="532.5" width="220.10999999999999" height="16.5" as="geometry"/></mxCell>
242 <mxCell id="03-mentions-commands-48" value="/garfield:email-letter" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="559.5" width="184.14" height="18.5" as="geometry"/></mxCell>242 <mxCell id="03-mentions-commands-48" value="/garfield:email-letter" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="559.5" width="184.14" height="18.5" as="geometry"/></mxCell>
243- <mxCell id="03-mentions-commands-49" value="[texte]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="721.09" y="561.5" width="49.91" height="16.5" as="geometry"/></mxCell>243+ <mxCell id="03-mentions-commands-49" value="[text]" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="728.22" y="561.5" width="42.78" height="16.5" as="geometry"/></mxCell>
244- <mxCell id="03-mentions-commands-50" value="E-mails et lettres : formules, tu ou vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="578.5" width="273.46999999999997" height="16.5" as="geometry"/></mxCell>244+ <mxCell id="03-mentions-commands-50" value="E-mails and letters: greetings, tu or vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="578.5" width="280.14" height="16.5" as="geometry"/></mxCell>
245 <mxCell id="03-mentions-commands-51" value="/alice:compact" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="605.5" width="117.17999999999999" height="18.5" as="geometry"/></mxCell>245 <mxCell id="03-mentions-commands-51" value="/alice:compact" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="289" y="605.5" width="117.17999999999999" height="18.5" as="geometry"/></mxCell>
246 <mxCell id="03-mentions-commands-52" value="" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="751" y="607.5" width="20" height="16.5" as="geometry"/></mxCell>246 <mxCell id="03-mentions-commands-52" value="" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="751" y="607.5" width="20" height="16.5" as="geometry"/></mxCell>
247- <mxCell id="03-mentions-commands-53" value="Résumer l'historique maintenant" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="624.5" width="206.76999999999998" height="16.5" as="geometry"/></mxCell>247+ <mxCell id="03-mentions-commands-53" value="Summarise the history now" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="289" y="624.5" width="166.75" height="16.5" as="geometry"/></mxCell>
248 <mxCell id="03-mentions-commands-54" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="660" width="969" height="64" as="geometry"/></mxCell>248 <mxCell id="03-mentions-commands-54" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FFFFFF;strokeColor=#3B6FE0;strokeWidth=1.5;html=1;" vertex="1" parent="1"><mxGeometry x="271" y="660" width="969" height="64" as="geometry"/></mxCell>
249 <mxCell id="03-mentions-commands-55" value="/gar" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="672" width="32.48" height="19" as="geometry"/></mxCell>249 <mxCell id="03-mentions-commands-55" value="/gar" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="672" width="32.48" height="19" as="geometry"/></mxCell>
250- <mxCell id="03-mentions-commands-56" value="Tab : /garfield:idioms · le texte après la commande est son argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="699" width="433.84" height="16" as="geometry"/></mxCell>250+ <mxCell id="03-mentions-commands-56" value="Tab: /garfield:idioms · the text after the command is its argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="285" y="699" width="421.08" height="16" as="geometry"/></mxCell>
251 <mxCell id="03-mentions-commands-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="693" y="168" width="24" height="24" as="geometry"/></mxCell>251 <mxCell id="03-mentions-commands-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="693" y="168" width="24" height="24" as="geometry"/></mxCell>
252 <mxCell id="03-mentions-commands-58" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="559" y="344" width="24" height="24" as="geometry"/></mxCell>252 <mxCell id="03-mentions-commands-58" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="559" y="344" width="24" height="24" as="geometry"/></mxCell>
253 <mxCell id="03-mentions-commands-59" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="793" y="458" width="24" height="24" as="geometry"/></mxCell>253 <mxCell id="03-mentions-commands-59" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="793" y="458" width="24" height="24" as="geometry"/></mxCell>
@@ -255,38 +255,38 @@
255 <mxCell id="03-mentions-commands-61" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1208" y="680" width="24" height="24" as="geometry"/></mxCell>255 <mxCell id="03-mentions-commands-61" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1208" y="680" width="24" height="24" as="geometry"/></mxCell>
256 <mxCell id="03-mentions-commands-62" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>256 <mxCell id="03-mentions-commands-62" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
257 <mxCell id="03-mentions-commands-63" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>257 <mxCell id="03-mentions-commands-63" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
258- <mxCell id="03-mentions-commands-64" value="@ liste les agents de la config" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="242.73" height="18.5" as="geometry"/></mxCell>258+ <mxCell id="03-mentions-commands-64" value="@ lists the configured agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="227.07" height="18.5" as="geometry"/></mxCell>
259- <mxCell id="03-mentions-commands-65" value="Nom technique + titre + description." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="250.55999999999997" height="17" as="geometry"/></mxCell>259+ <mxCell id="03-mentions-commands-65" value="Technical name + title + description." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="257.52" height="17" as="geometry"/></mxCell>
260- <mxCell id="03-mentions-commands-66" value="Filtrage flou sur nom et titre (« gar »)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="285.35999999999996" height="17" as="geometry"/></mxCell>260+ <mxCell id="03-mentions-commands-66" value="Fuzzy filter on name and title (&quot;gar&quot;)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="271.44" height="17" as="geometry"/></mxCell>
261 <mxCell id="03-mentions-commands-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>261 <mxCell id="03-mentions-commands-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
262 <mxCell id="03-mentions-commands-68" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>262 <mxCell id="03-mentions-commands-68" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
263- <mxCell id="03-mentions-commands-69" value="Tab sélectionne, sinon on écrit" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="242.73" height="18.5" as="geometry"/></mxCell>263+ <mxCell id="03-mentions-commands-69" value="Tab picks, otherwise you type" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="227.07" height="18.5" as="geometry"/></mxCell>
264- <mxCell id="03-mentions-commands-70" value="Comportement demandé dans le draft (#1)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="278.4" height="17" as="geometry"/></mxCell>264+ <mxCell id="03-mentions-commands-70" value="Behaviour asked for in the draft (#1)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="264.47999999999996" height="17" as="geometry"/></mxCell>
265- <mxCell id="03-mentions-commands-71" value="Échap ferme sans rien insérer." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="208.79999999999998" height="17" as="geometry"/></mxCell>265+ <mxCell id="03-mentions-commands-71" value="Esc closes without inserting anything." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="264.47999999999996" height="17" as="geometry"/></mxCell>
266 <mxCell id="03-mentions-commands-72" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>266 <mxCell id="03-mentions-commands-72" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
267 <mxCell id="03-mentions-commands-73" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>267 <mxCell id="03-mentions-commands-73" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
268- <mxCell id="03-mentions-commands-74" value="/agent:commande en multi-agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="242.73" height="18.5" as="geometry"/></mxCell>268+ <mxCell id="03-mentions-commands-74" value="/agent:command with several agents" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="266.21999999999997" height="18.5" as="geometry"/></mxCell>
269- <mxCell id="03-mentions-commands-75" value="Avec un seul agent : /commande. Sidekicks" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="285.35999999999996" height="17" as="geometry"/></mxCell>269+ <mxCell id="03-mentions-commands-75" value="With one agent: /command. Sidekicks" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="243.6" height="17" as="geometry"/></mxCell>
270- <mxCell id="03-mentions-commands-76" value="envoie « /idioms … » au seul Garfield." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>270+ <mxCell id="03-mentions-commands-76" value="sends &quot;/idioms …&quot; to Garfield only." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="243.6" height="17" as="geometry"/></mxCell>
271 <mxCell id="03-mentions-commands-77" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>271 <mxCell id="03-mentions-commands-77" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
272 <mxCell id="03-mentions-commands-78" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>272 <mxCell id="03-mentions-commands-78" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
273- <mxCell id="03-mentions-commands-79" value="Skills et commandes intégrées" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="227.07" height="18.5" as="geometry"/></mxCell>273+ <mxCell id="03-mentions-commands-79" value="Skills and built-in commands" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="219.23999999999998" height="18.5" as="geometry"/></mxCell>
274- <mxCell id="03-mentions-commands-80" value="mm annonce compact, new, unload et ses" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="264.47999999999996" height="17" as="geometry"/></mxCell>274+ <mxCell id="03-mentions-commands-80" value="mm announces compact, new, unload and" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="257.52" height="17" as="geometry"/></mxCell>
275- <mxCell id="03-mentions-commands-81" value="skills (available_commands_update)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="243.6" height="17" as="geometry"/></mxCell>275+ <mxCell id="03-mentions-commands-81" value="its skills (available_commands_update)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="271.44" height="17" as="geometry"/></mxCell>
276 <mxCell id="03-mentions-commands-82" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>276 <mxCell id="03-mentions-commands-82" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>
277 <mxCell id="03-mentions-commands-83" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>277 <mxCell id="03-mentions-commands-83" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>
278- <mxCell id="03-mentions-commands-84" value="Indice d'argument" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="133.10999999999999" height="18.5" as="geometry"/></mxCell>278+ <mxCell id="03-mentions-commands-84" value="Argument hint" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>
279- <mxCell id="03-mentions-commands-85" value="input.hint de la commande ACP, affiché" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="264.47999999999996" height="17" as="geometry"/></mxCell>279+ <mxCell id="03-mentions-commands-85" value="The ACP command's input.hint, shown" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="243.6" height="17" as="geometry"/></mxCell>
280- <mxCell id="03-mentions-commands-86" value="tant que l'argument n'est pas saisi." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>280+ <mxCell id="03-mentions-commands-86" value="until the argument is typed." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="194.88" height="17" as="geometry"/></mxCell>
281 </root>281 </root>
282 </mxGraphModel>282 </mxGraphModel>
283 </diagram>283 </diagram>
284- <diagram id="04-permission" name="4 · Demande de permission">284+ <diagram id="04-permission" name="4 · Permission request">
285 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">285 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
286 <root>286 <root>
287 <mxCell id="0"/>287 <mxCell id="0"/>
288 <mxCell id="1" parent="0"/>288 <mxCell id="1" parent="0"/>
289- <mxCell id="04-permission-title" value="&lt;b&gt;4 · Demande de permission&lt;/b&gt;&lt;br&gt;&lt;i&gt;En langage clair, sur la conversation concernée, avec une sortie de secours&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>289+ <mxCell id="04-permission-title" value="&lt;b&gt;4 · Permission request&lt;/b&gt;&lt;br&gt;&lt;i&gt;In plain words, on the conversation concerned, with a way out&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
290 <mxCell id="04-permission-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>290 <mxCell id="04-permission-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
291 <mxCell id="04-permission-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>291 <mxCell id="04-permission-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
292 <mxCell id="04-permission-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>292 <mxCell id="04-permission-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -295,13 +295,13 @@
295 <mxCell id="04-permission-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>295 <mxCell id="04-permission-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
296 <mxCell id="04-permission-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>296 <mxCell id="04-permission-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
297 <mxCell id="04-permission-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>297 <mxCell id="04-permission-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
298- <mxCell id="04-permission-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>298+ <mxCell id="04-permission-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
299 <mxCell id="04-permission-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>299 <mxCell id="04-permission-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
300 <mxCell id="04-permission-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>300 <mxCell id="04-permission-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
301 <mxCell id="04-permission-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>301 <mxCell id="04-permission-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
302 <mxCell id="04-permission-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>302 <mxCell id="04-permission-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
303- <mxCell id="04-permission-14" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>303+ <mxCell id="04-permission-14" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
304- <mxCell id="04-permission-15" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>304+ <mxCell id="04-permission-15" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
305 <mxCell id="04-permission-16" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="218" width="214" height="26" as="geometry"/></mxCell>305 <mxCell id="04-permission-16" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="28" y="218" width="214" height="26" as="geometry"/></mxCell>
306 <mxCell id="04-permission-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>306 <mxCell id="04-permission-17" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
307 <mxCell id="04-permission-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>307 <mxCell id="04-permission-18" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
@@ -315,28 +315,28 @@
315 <mxCell id="04-permission-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>315 <mxCell id="04-permission-26" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
316 <mxCell id="04-permission-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>316 <mxCell id="04-permission-27" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="44" as="geometry"/></mxCell>
317 <mxCell id="04-permission-28" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="52.199999999999996" height="20" as="geometry"/></mxCell>317 <mxCell id="04-permission-28" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="129" width="52.199999999999996" height="20" as="geometry"/></mxCell>
318- <mxCell id="04-permission-29" value="Vous" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="31.319999999999997" height="18.5" as="geometry"/></mxCell>318+ <mxCell id="04-permission-29" value="You" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="182.5" width="23.49" height="18.5" as="geometry"/></mxCell>
319- <mxCell id="04-permission-30" value="@riker supprime les fichiers temporaires du dossier" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="399.33" height="18.5" as="geometry"/></mxCell>319+ <mxCell id="04-permission-30" value="@riker delete the temporary files in the folder" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="275" y="204.5" width="368.01" height="18.5" as="geometry"/></mxCell>
320 <mxCell id="04-permission-31" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="70.47" height="18.5" as="geometry"/></mxCell>320 <mxCell id="04-permission-31" value="🧑‍🚀 Riker" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#5E35B1;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="238.5" width="70.47" height="18.5" as="geometry"/></mxCell>
321- <mxCell id="04-permission-32" value="attend votre réponse" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#C98A1B;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="239.5" width="145.0" height="17.5" as="geometry"/></mxCell>321+ <mxCell id="04-permission-32" value="waiting for your answer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#C98A1B;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="365" y="239.5" width="166.75" height="17.5" as="geometry"/></mxCell>
322 <mxCell id="04-permission-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#C98A1B;strokeWidth=2;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="270" width="720" height="330" as="geometry"/></mxCell>322 <mxCell id="04-permission-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#C98A1B;strokeWidth=2;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="275" y="270" width="720" height="330" as="geometry"/></mxCell>
323- <mxCell id="04-permission-34" value="🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="288" width="528.9599999999999" height="21" as="geometry"/></mxCell>323+ <mxCell id="04-permission-34" value="🧑‍🚀 Riker wants to run a command on your computer" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="288" width="454.71999999999997" height="21" as="geometry"/></mxCell>
324 <mxCell id="04-permission-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="322" width="680" height="40" as="geometry"/></mxCell>324 <mxCell id="04-permission-35" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="322" width="680" height="40" as="geometry"/></mxCell>
325 <mxCell id="04-permission-36" value="rm -f *.tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="309" y="333" width="95.48" height="19" as="geometry"/></mxCell>325 <mxCell id="04-permission-36" value="rm -f *.tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="309" y="333" width="95.48" height="19" as="geometry"/></mxCell>
326- <mxCell id="04-permission-37" value="Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="375" width="580.5799999999999" height="18" as="geometry"/></mxCell>326+ <mxCell id="04-permission-37" value="What it does: permanently deletes the files ending in .tmp" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="375" width="437.32" height="18" as="geometry"/></mxCell>
327- <mxCell id="04-permission-38" value="dans agents/riker . Ils ne passent pas par la corbeille." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="395" width="429.78" height="18" as="geometry"/></mxCell>327+ <mxCell id="04-permission-38" value="in agents/riker . They do not go to the trash." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="395" width="354.38" height="18" as="geometry"/></mxCell>
328- <mxCell id="04-permission-39" value="⚠ Action irréversible" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="425" width="165.88" height="18" as="geometry"/></mxCell>328+ <mxCell id="04-permission-39" value="⚠ Cannot be undone" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="295" y="425" width="143.26" height="18" as="geometry"/></mxCell>
329 <mxCell id="04-permission-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="460" width="170" height="32" as="geometry"/></mxCell>329 <mxCell id="04-permission-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="460" width="170" height="32" as="geometry"/></mxCell>
330- <mxCell id="04-permission-41" value="Autoriser une fois" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="312.14" y="468.0" width="135.72" height="18" as="geometry"/></mxCell>330+ <mxCell id="04-permission-41" value="Allow once" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="342.3" y="468.0" width="75.39999999999999" height="18" as="geometry"/></mxCell>
331 <mxCell id="04-permission-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="460" width="300" height="32" as="geometry"/></mxCell>331 <mxCell id="04-permission-42" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="460" width="300" height="32" as="geometry"/></mxCell>
332- <mxCell id="04-permission-43" value="Toujours autoriser bash ici" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="523.21" y="468.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>332+ <mxCell id="04-permission-43" value="Always allow bash here" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="542.06" y="468.0" width="165.88" height="18" as="geometry"/></mxCell>
333 <mxCell id="04-permission-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="504" width="170" height="32" as="geometry"/></mxCell>333 <mxCell id="04-permission-44" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="295" y="504" width="170" height="32" as="geometry"/></mxCell>
334- <mxCell id="04-permission-45" value="Refuser" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="353.61" y="512.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>334+ <mxCell id="04-permission-45" value="Reject" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="357.38" y="512.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
335 <mxCell id="04-permission-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="504" width="300" height="32" as="geometry"/></mxCell>335 <mxCell id="04-permission-46" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="475" y="504" width="300" height="32" as="geometry"/></mxCell>
336- <mxCell id="04-permission-47" value="Ne plus jamais proposer bash ici" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="504.36" y="512.0" width="241.27999999999997" height="18" as="geometry"/></mxCell>336+ <mxCell id="04-permission-47" value="Never offer bash here again" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="523.21" y="512.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>
337 <mxCell id="04-permission-48" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="795" y="504" width="180" height="32" as="geometry"/></mxCell>337 <mxCell id="04-permission-48" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="795" y="504" width="180" height="32" as="geometry"/></mxCell>
338- <mxCell id="04-permission-49" value="■ Arrêter Riker" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="828.45" y="512.0" width="113.1" height="18" as="geometry"/></mxCell>338+ <mxCell id="04-permission-49" value="■ Stop Riker" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="839.76" y="512.0" width="90.47999999999999" height="18" as="geometry"/></mxCell>
339- <mxCell id="04-permission-50" value="▸ Détails techniques (tool call bash, rawInput)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="558" width="327.12" height="17" as="geometry"/></mxCell>339+ <mxCell id="04-permission-50" value="▸ Technical details (bash tool call, rawInput)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295" y="558" width="320.15999999999997" height="17" as="geometry"/></mxCell>
340 <mxCell id="04-permission-51" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="967" y="288" width="24" height="24" as="geometry"/></mxCell>340 <mxCell id="04-permission-51" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="967" y="288" width="24" height="24" as="geometry"/></mxCell>
341 <mxCell id="04-permission-52" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="943" y="386" width="24" height="24" as="geometry"/></mxCell>341 <mxCell id="04-permission-52" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="943" y="386" width="24" height="24" as="geometry"/></mxCell>
342 <mxCell id="04-permission-53" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="775" y="464" width="24" height="24" as="geometry"/></mxCell>342 <mxCell id="04-permission-53" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="775" y="464" width="24" height="24" as="geometry"/></mxCell>
@@ -344,39 +344,39 @@
344 <mxCell id="04-permission-55" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="643" y="554" width="24" height="24" as="geometry"/></mxCell>344 <mxCell id="04-permission-55" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="643" y="554" width="24" height="24" as="geometry"/></mxCell>
345 <mxCell id="04-permission-56" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>345 <mxCell id="04-permission-56" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
346 <mxCell id="04-permission-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>346 <mxCell id="04-permission-57" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
347- <mxCell id="04-permission-58" value="Attaché à la conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="195.75" height="18.5" as="geometry"/></mxCell>347+ <mxCell id="04-permission-58" value="Attached to the conversation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="219.23999999999998" height="18.5" as="geometry"/></mxCell>
348- <mxCell id="04-permission-59" value="Bloque seulement la session de Riker ; les" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>348+ <mxCell id="04-permission-59" value="Blocks only Riker's session; the other" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="264.47999999999996" height="17" as="geometry"/></mxCell>
349- <mxCell id="04-permission-60" value="autres channels continuent (#17)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="229.67999999999998" height="17" as="geometry"/></mxCell>349+ <mxCell id="04-permission-60" value="channels go on (#17)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="146.16" height="17" as="geometry"/></mxCell>
350 <mxCell id="04-permission-61" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="94" as="geometry"/></mxCell>350 <mxCell id="04-permission-61" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="94" as="geometry"/></mxCell>
351 <mxCell id="04-permission-62" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>351 <mxCell id="04-permission-62" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
352- <mxCell id="04-permission-63" value="Expliquer, pas montrer du JSON" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>352+ <mxCell id="04-permission-63" value="Explain, do not show JSON" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="195.75" height="18.5" as="geometry"/></mxCell>
353- <mxCell id="04-permission-64" value="Titre ACP + kind + rawInput traduits en" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="271.44" height="17" as="geometry"/></mxCell>353+ <mxCell id="04-permission-64" value="ACP title + kind + rawInput turned into" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="271.44" height="17" as="geometry"/></mxCell>
354- <mxCell id="04-permission-65" value="phrase. Les actions destructives sont" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="257.52" height="17" as="geometry"/></mxCell>354+ <mxCell id="04-permission-65" value="a sentence. Destructive actions are" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="243.6" height="17" as="geometry"/></mxCell>
355- <mxCell id="04-permission-66" value="signalées." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="244" width="69.6" height="17" as="geometry"/></mxCell>355+ <mxCell id="04-permission-66" value="flagged." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="244" width="55.67999999999999" height="17" as="geometry"/></mxCell>
356 <mxCell id="04-permission-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="278" width="356" height="94" as="geometry"/></mxCell>356 <mxCell id="04-permission-67" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="278" width="356" height="94" as="geometry"/></mxCell>
357 <mxCell id="04-permission-68" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="288" width="24" height="24" as="geometry"/></mxCell>357 <mxCell id="04-permission-68" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="288" width="24" height="24" as="geometry"/></mxCell>
358- <mxCell id="04-permission-69" value="Les 4 options ACP, par ids" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="291.5" width="203.57999999999998" height="18.5" as="geometry"/></mxCell>358+ <mxCell id="04-permission-69" value="The 4 ACP options, by id" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="291.5" width="187.92" height="18.5" as="geometry"/></mxCell>
359 <mxCell id="04-permission-70" value="allow · allow_always · reject · never." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>359 <mxCell id="04-permission-70" value="allow · allow_always · reject · never." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="264.47999999999996" height="17" as="geometry"/></mxCell>
360- <mxCell id="04-permission-71" value="« Ne plus jamais » est mis en avant au" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="264.47999999999996" height="17" as="geometry"/></mxCell>360+ <mxCell id="04-permission-71" value="&quot;Never again&quot; is put forward rather" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="334" width="243.6" height="17" as="geometry"/></mxCell>
361- <mxCell id="04-permission-72" value="lieu de « Refuser une fois »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="352" width="201.83999999999997" height="17" as="geometry"/></mxCell>361+ <mxCell id="04-permission-72" value="than &quot;Reject once&quot;." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="352" width="132.23999999999998" height="17" as="geometry"/></mxCell>
362 <mxCell id="04-permission-73" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="386" width="356" height="76" as="geometry"/></mxCell>362 <mxCell id="04-permission-73" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="386" width="356" height="76" as="geometry"/></mxCell>
363 <mxCell id="04-permission-74" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="396" width="24" height="24" as="geometry"/></mxCell>363 <mxCell id="04-permission-74" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="396" width="24" height="24" as="geometry"/></mxCell>
364- <mxCell id="04-permission-75" value="Toujours une sortie de secours" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="399.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>364+ <mxCell id="04-permission-75" value="Always a way out" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="399.5" width="125.27999999999999" height="18.5" as="geometry"/></mxCell>
365- <mxCell id="04-permission-76" value="Spike ACP : après un refus, un petit modèle" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="299.28" height="17" as="geometry"/></mxCell>365+ <mxCell id="04-permission-76" value="ACP spike: after a rejection, a small model" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="424" width="299.28" height="17" as="geometry"/></mxCell>
366- <mxCell id="04-permission-77" value="a réessayé 40 fois. Arrêter = session/cancel." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="442" width="313.2" height="17" as="geometry"/></mxCell>366+ <mxCell id="04-permission-77" value="retried 40 times. Stop = session/cancel." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="442" width="278.4" height="17" as="geometry"/></mxCell>
367 <mxCell id="04-permission-78" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="476" width="356" height="58" as="geometry"/></mxCell>367 <mxCell id="04-permission-78" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="476" width="356" height="58" as="geometry"/></mxCell>
368 <mxCell id="04-permission-79" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="486" width="24" height="24" as="geometry"/></mxCell>368 <mxCell id="04-permission-79" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="486" width="24" height="24" as="geometry"/></mxCell>
369- <mxCell id="04-permission-80" value="Détails pour les curieux" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="489.5" width="187.92" height="18.5" as="geometry"/></mxCell>369+ <mxCell id="04-permission-80" value="Details for the curious" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="489.5" width="180.08999999999997" height="18.5" as="geometry"/></mxCell>
370- <mxCell id="04-permission-81" value="Repliés par défaut." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="132.23999999999998" height="17" as="geometry"/></mxCell>370+ <mxCell id="04-permission-81" value="Folded by default." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="514" width="125.27999999999999" height="17" as="geometry"/></mxCell>
371 </root>371 </root>
372 </mxGraphModel>372 </mxGraphModel>
373 </diagram>373 </diagram>
374- <diagram id="05-agents-panel" name="5 · Panel Agents : fichiers, éditeur, terminal">374+ <diagram id="05-agents-panel" name="5 · Agents panel: files, editor, terminal">
375 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">375 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
376 <root>376 <root>
377 <mxCell id="0"/>377 <mxCell id="0"/>
378 <mxCell id="1" parent="0"/>378 <mxCell id="1" parent="0"/>
379- <mxCell id="05-agents-panel-title" value="&lt;b&gt;5 · Panel Agents : fichiers, éditeur, terminal&lt;/b&gt;&lt;br&gt;&lt;i&gt;Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>379+ <mxCell id="05-agents-panel-title" value="&lt;b&gt;5 · Agents panel: files, editor, terminal&lt;/b&gt;&lt;br&gt;&lt;i&gt;The agent's workspace, whether it runs locally, in a container or in an sbx sandbox&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
380 <mxCell id="05-agents-panel-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>380 <mxCell id="05-agents-panel-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
381 <mxCell id="05-agents-panel-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>381 <mxCell id="05-agents-panel-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
382 <mxCell id="05-agents-panel-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>382 <mxCell id="05-agents-panel-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -385,13 +385,13 @@
385 <mxCell id="05-agents-panel-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>385 <mxCell id="05-agents-panel-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
386 <mxCell id="05-agents-panel-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>386 <mxCell id="05-agents-panel-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
387 <mxCell id="05-agents-panel-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>387 <mxCell id="05-agents-panel-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
388- <mxCell id="05-agents-panel-9" value="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="366.84999999999997" height="16.5" as="geometry"/></mxCell>388+ <mxCell id="05-agents-panel-9" value="● 2 of 2 agents running · config: ~/agents/team.yaml" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="346.84" height="16.5" as="geometry"/></mxCell>
389 <mxCell id="05-agents-panel-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>389 <mxCell id="05-agents-panel-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
390 <mxCell id="05-agents-panel-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>390 <mxCell id="05-agents-panel-11" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="116" width="230" height="718" as="geometry"/></mxCell>
391 <mxCell id="05-agents-panel-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>391 <mxCell id="05-agents-panel-12" value="CHANNELS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="133" width="51.04" height="16" as="geometry"/></mxCell>
392 <mxCell id="05-agents-panel-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>392 <mxCell id="05-agents-panel-13" value="+" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="214" y="128" width="20" height="21" as="geometry"/></mxCell>
393- <mxCell id="05-agents-panel-14" value="# général" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>393+ <mxCell id="05-agents-panel-14" value="# general" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="162.5" width="70.47" height="18.5" as="geometry"/></mxCell>
394- <mxCell id="05-agents-panel-15" value="# traductions" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="101.78999999999999" height="18.5" as="geometry"/></mxCell>394+ <mxCell id="05-agents-panel-15" value="# translations" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="192.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
395 <mxCell id="05-agents-panel-16" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>395 <mxCell id="05-agents-panel-16" value="# code" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="38" y="222.5" width="46.98" height="18.5" as="geometry"/></mxCell>
396 <mxCell id="05-agents-panel-17" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>396 <mxCell id="05-agents-panel-17" value="AGENTS" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="36" y="263" width="38.279999999999994" height="16" as="geometry"/></mxCell>
397 <mxCell id="05-agents-panel-18" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>397 <mxCell id="05-agents-panel-18" value="" style="ellipse;fillColor=#3E9B4F;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="296.5" width="9" height="9" as="geometry"/></mxCell>
@@ -403,15 +403,15 @@
403 <mxCell id="05-agents-panel-24" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>403 <mxCell id="05-agents-panel-24" value="" style="ellipse;fillColor=#C98A1B;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="37.5" y="386.5" width="9" height="9" as="geometry"/></mxCell>
404 <mxCell id="05-agents-panel-25" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>404 <mxCell id="05-agents-panel-25" value="🐮 Garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="54" y="382.5" width="78.3" height="18.5" as="geometry"/></mxCell>
405 <mxCell id="05-agents-panel-26" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="56" as="geometry"/></mxCell>405 <mxCell id="05-agents-panel-26" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="116" width="1009" height="56" as="geometry"/></mxCell>
406- <mxCell id="05-agents-panel-27" value="🐮 Garfield (traducteur)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="126" width="213.44" height="21" as="geometry"/></mxCell>406+ <mxCell id="05-agents-panel-27" value="🐮 Garfield (translator)" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="267" y="126" width="213.44" height="21" as="geometry"/></mxCell>
407- <mxCell id="05-agents-panel-28" value="● occupé · local · agents/garfield · gemma-4-E4B" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#C98A1B;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="267" y="150" width="334.08" height="17" as="geometry"/></mxCell>407+ <mxCell id="05-agents-panel-28" value="● busy · local · agents/garfield · gemma-4-E4B" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#C98A1B;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="267" y="150" width="320.15999999999997" height="17" as="geometry"/></mxCell>
408 <mxCell id="05-agents-panel-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="128" width="110" height="32" as="geometry"/></mxCell>408 <mxCell id="05-agents-panel-29" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="128" width="110" height="32" as="geometry"/></mxCell>
409- <mxCell id="05-agents-panel-30" value="Redémarrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1027.3" y="136.0" width="75.39999999999999" height="18" as="geometry"/></mxCell>409+ <mxCell id="05-agents-panel-30" value="Restart" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1038.61" y="136.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>
410 <mxCell id="05-agents-panel-31" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="128" width="110" height="32" as="geometry"/></mxCell>410 <mxCell id="05-agents-panel-31" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="128" width="110" height="32" as="geometry"/></mxCell>
411- <mxCell id="05-agents-panel-32" value="Arrêter" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1158.61" y="136.0" width="52.779999999999994" height="18" as="geometry"/></mxCell>411+ <mxCell id="05-agents-panel-32" value="Stop" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1169.92" y="136.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
412- <mxCell id="05-agents-panel-33" value="Fichiers" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#3B6FE0;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="184.5" width="62.63999999999999" height="18.5" as="geometry"/></mxCell>412+ <mxCell id="05-agents-panel-33" value="Files" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#3B6FE0;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="275" y="184.5" width="39.15" height="18.5" as="geometry"/></mxCell>
413 <mxCell id="05-agents-panel-34" value="Terminal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="375" y="184.5" width="62.63999999999999" height="18.5" as="geometry"/></mxCell>413 <mxCell id="05-agents-panel-34" value="Terminal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="375" y="184.5" width="62.63999999999999" height="18.5" as="geometry"/></mxCell>
414- <mxCell id="05-agents-panel-35" value="Journal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="475" y="184.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>414+ <mxCell id="05-agents-panel-35" value="Log" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="475" y="184.5" width="23.49" height="18.5" as="geometry"/></mxCell>
415 <mxCell id="05-agents-panel-36" value="" style="rounded=0;fillColor=#3B6FE0;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="267" y="206" width="70" height="2" as="geometry"/></mxCell>415 <mxCell id="05-agents-panel-36" value="" style="rounded=0;fillColor=#3B6FE0;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="267" y="206" width="70" height="2" as="geometry"/></mxCell>
416 <mxCell id="05-agents-panel-37" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="216" width="260" height="420" as="geometry"/></mxCell>416 <mxCell id="05-agents-panel-37" value="" style="rounded=0;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="216" width="260" height="420" as="geometry"/></mxCell>
417 <mxCell id="05-agents-panel-38" value="▾ garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="232.5" width="77.5" height="17.5" as="geometry"/></mxCell>417 <mxCell id="05-agents-panel-38" value="▾ garfield" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="232.5" width="77.5" height="17.5" as="geometry"/></mxCell>
@@ -424,20 +424,20 @@
424 <mxCell id="05-agents-panel-45" value=" ▸ idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="388.5" width="93.0" height="17.5" as="geometry"/></mxCell>424 <mxCell id="05-agents-panel-45" value=" ▸ idioms" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="388.5" width="93.0" height="17.5" as="geometry"/></mxCell>
425 <mxCell id="05-agents-panel-46" value=" ▸ styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="414.5" width="93.0" height="17.5" as="geometry"/></mxCell>425 <mxCell id="05-agents-panel-46" value=" ▸ styles" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="414.5" width="93.0" height="17.5" as="geometry"/></mxCell>
426 <mxCell id="05-agents-panel-47" value=" ▸ technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="440.5" width="155.0" height="17.5" as="geometry"/></mxCell>426 <mxCell id="05-agents-panel-47" value=" ▸ technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="440.5" width="155.0" height="17.5" as="geometry"/></mxCell>
427- <mxCell id="05-agents-panel-48" value=" traduction.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="466.5" width="131.75" height="17.5" as="geometry"/></mxCell>427+ <mxCell id="05-agents-panel-48" value=" translation.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="466.5" width="139.5" height="17.5" as="geometry"/></mxCell>
428- <mxCell id="05-agents-panel-49" value="+ Nouveau fichier" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3B6FE0;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="503.5" width="123.24999999999999" height="17.5" as="geometry"/></mxCell>428+ <mxCell id="05-agents-panel-49" value="+ New file" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#3B6FE0;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="265" y="503.5" width="72.5" height="17.5" as="geometry"/></mxCell>
429 <mxCell id="05-agents-panel-50" value="" style="rounded=0;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="521" y="216" width="729" height="420" as="geometry"/></mxCell>429 <mxCell id="05-agents-panel-50" value="" style="rounded=0;fillColor=#F4F5F7;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="521" y="216" width="729" height="420" as="geometry"/></mxCell>
430 <mxCell id="05-agents-panel-51" value="AGENTS.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="535" y="227" width="67.86" height="18" as="geometry"/></mxCell>430 <mxCell id="05-agents-panel-51" value="AGENTS.md" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="535" y="227" width="67.86" height="18" as="geometry"/></mxCell>
431- <mxCell id="05-agents-panel-52" value="# Style de traduction" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="257.5" width="162.75" height="17.5" as="geometry"/></mxCell>431+ <mxCell id="05-agents-panel-52" value="# Translation style" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="257.5" width="147.25" height="17.5" as="geometry"/></mxCell>
432 <mxCell id="05-agents-panel-53" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="279.5" width="20" height="17.5" as="geometry"/></mxCell>432 <mxCell id="05-agents-panel-53" value="" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="279.5" width="20" height="17.5" as="geometry"/></mxCell>
433- <mxCell id="05-agents-panel-54" value="- Registre : celui du texte source." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="301.5" width="271.25" height="17.5" as="geometry"/></mxCell>433+ <mxCell id="05-agents-panel-54" value="- Register: the one of the source text." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="301.5" width="302.25" height="17.5" as="geometry"/></mxCell>
434- <mxCell id="05-agents-panel-55" value="- Typographie française : espaces insécables" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="323.5" width="341.0" height="17.5" as="geometry"/></mxCell>434+ <mxCell id="05-agents-panel-55" value="- French typography: non-breaking spaces" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="323.5" width="310.0" height="17.5" as="geometry"/></mxCell>
435- <mxCell id="05-agents-panel-56" value=" avant ; : ! ? et entre « guillemets »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="345.5" width="310.0" height="17.5" as="geometry"/></mxCell>435+ <mxCell id="05-agents-panel-56" value=" before ; : ! ? and inside « quotes »." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="345.5" width="302.25" height="17.5" as="geometry"/></mxCell>
436- <mxCell id="05-agents-panel-57" value="- Ne jamais traduire le code ni les noms propres." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="367.5" width="379.75" height="17.5" as="geometry"/></mxCell>436+ <mxCell id="05-agents-panel-57" value="- Never translate code or proper names." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#1D1F24;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="535" y="367.5" width="302.25" height="17.5" as="geometry"/></mxCell>
437 <mxCell id="05-agents-panel-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="590" width="110" height="32" as="geometry"/></mxCell>437 <mxCell id="05-agents-panel-58" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1010" y="590" width="110" height="32" as="geometry"/></mxCell>
438- <mxCell id="05-agents-panel-59" value="Enregistrer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1023.53" y="598.0" width="82.94" height="18" as="geometry"/></mxCell>438+ <mxCell id="05-agents-panel-59" value="Save" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1049.92" y="598.0" width="30.159999999999997" height="18" as="geometry"/></mxCell>
439 <mxCell id="05-agents-panel-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="590" width="110" height="32" as="geometry"/></mxCell>439 <mxCell id="05-agents-panel-60" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1130" y="590" width="110" height="32" as="geometry"/></mxCell>
440- <mxCell id="05-agents-panel-61" value="Supprimer" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1151.07" y="598.0" width="67.86" height="18" as="geometry"/></mxCell>440+ <mxCell id="05-agents-panel-61" value="Delete" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#D64561;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1162.38" y="598.0" width="45.239999999999995" height="18" as="geometry"/></mxCell>
441 <mxCell id="05-agents-panel-62" value="" style="rounded=0;fillColor=#111317;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="646" width="1009" height="188" as="geometry"/></mxCell>441 <mxCell id="05-agents-panel-62" value="" style="rounded=0;fillColor=#111317;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="251" y="646" width="1009" height="188" as="geometry"/></mxCell>
442 <mxCell id="05-agents-panel-63" value="garfield $ ls skills" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="659.5" width="155.0" height="17.5" as="geometry"/></mxCell>442 <mxCell id="05-agents-panel-63" value="garfield $ ls skills" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="659.5" width="155.0" height="17.5" as="geometry"/></mxCell>
443 <mxCell id="05-agents-panel-64" value="email-letter idioms styles technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="679.5" width="341.0" height="17.5" as="geometry"/></mxCell>443 <mxCell id="05-agents-panel-64" value="email-letter idioms styles technical-text" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12.5;fontColor=#D7DAE0;fontStyle=0;fontFamily=DejaVu Sans Mono;" vertex="1" parent="1"><mxGeometry x="265" y="679.5" width="341.0" height="17.5" as="geometry"/></mxCell>
@@ -449,38 +449,38 @@
449 <mxCell id="05-agents-panel-70" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="569" y="186" width="24" height="24" as="geometry"/></mxCell>449 <mxCell id="05-agents-panel-70" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="569" y="186" width="24" height="24" as="geometry"/></mxCell>
450 <mxCell id="05-agents-panel-71" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>450 <mxCell id="05-agents-panel-71" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
451 <mxCell id="05-agents-panel-72" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>451 <mxCell id="05-agents-panel-72" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
452- <mxCell id="05-agents-panel-73" value="État et emplacement de l'agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>452+ <mxCell id="05-agents-panel-73" value="The agent's state and location" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
453- <mxCell id="05-agents-panel-74" value="Statut temps réel (#11), runtime (local," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="278.4" height="17" as="geometry"/></mxCell>453+ <mxCell id="05-agents-panel-74" value="Live status (#11), runtime (local," style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="236.64" height="17" as="geometry"/></mxCell>
454- <mxCell id="05-agents-panel-75" value="container, sbx), dossier et modèle." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="243.6" height="17" as="geometry"/></mxCell>454+ <mxCell id="05-agents-panel-75" value="container, sbx), folder and model." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="236.64" height="17" as="geometry"/></mxCell>
455 <mxCell id="05-agents-panel-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>455 <mxCell id="05-agents-panel-76" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
456 <mxCell id="05-agents-panel-77" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>456 <mxCell id="05-agents-panel-77" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
457- <mxCell id="05-agents-panel-78" value="Arbre du workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="140.94" height="18.5" as="geometry"/></mxCell>457+ <mxCell id="05-agents-panel-78" value="Workspace tree" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="109.61999999999999" height="18.5" as="geometry"/></mxCell>
458- <mxCell id="05-agents-panel-79" value="Chargement paresseux, rafraîchi quand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="257.52" height="17" as="geometry"/></mxCell>458+ <mxCell id="05-agents-panel-79" value="Loaded lazily, refreshed when the" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="229.67999999999998" height="17" as="geometry"/></mxCell>
459- <mxCell id="05-agents-panel-80" value="l'agent modifie un fichier (#29)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="229.67999999999998" height="17" as="geometry"/></mxCell>459+ <mxCell id="05-agents-panel-80" value="agent changes a file (#29)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="187.92" height="17" as="geometry"/></mxCell>
460 <mxCell id="05-agents-panel-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>460 <mxCell id="05-agents-panel-81" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
461 <mxCell id="05-agents-panel-82" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>461 <mxCell id="05-agents-panel-82" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
462- <mxCell id="05-agents-panel-83" value="Éditer, supprimer avec confirmation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="274.04999999999995" height="18.5" as="geometry"/></mxCell>462+ <mxCell id="05-agents-panel-83" value="Edit, delete with confirmation" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="234.89999999999998" height="18.5" as="geometry"/></mxCell>
463- <mxCell id="05-agents-panel-84" value="Même éditeur que Settings. Conflit signalé" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="292.32" height="17" as="geometry"/></mxCell>463+ <mxCell id="05-agents-panel-84" value="Same editor as Settings. A conflict is" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="264.47999999999996" height="17" as="geometry"/></mxCell>
464- <mxCell id="05-agents-panel-85" value="si l'agent modifie le fichier (#30)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="250.55999999999997" height="17" as="geometry"/></mxCell>464+ <mxCell id="05-agents-panel-85" value="flagged if the agent changes the file (#30)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="306.23999999999995" height="17" as="geometry"/></mxCell>
465 <mxCell id="05-agents-panel-86" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>465 <mxCell id="05-agents-panel-86" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
466 <mxCell id="05-agents-panel-87" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>466 <mxCell id="05-agents-panel-87" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
467- <mxCell id="05-agents-panel-88" value="Terminal sur le workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="195.75" height="18.5" as="geometry"/></mxCell>467+ <mxCell id="05-agents-panel-88" value="Terminal in the workspace" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="195.75" height="18.5" as="geometry"/></mxCell>
468- <mxCell id="05-agents-panel-89" value="PTY local, docker exec ou sbx selon le" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="264.47999999999996" height="17" as="geometry"/></mxCell>468+ <mxCell id="05-agents-panel-89" value="Local PTY, docker exec or sbx depending on" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="292.32" height="17" as="geometry"/></mxCell>
469- <mxCell id="05-agents-panel-90" value="runtime (#28, #31). Replié par défaut." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="264.47999999999996" height="17" as="geometry"/></mxCell>469+ <mxCell id="05-agents-panel-90" value="the runtime (#28, #31). Folded by default." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="292.32" height="17" as="geometry"/></mxCell>
470 <mxCell id="05-agents-panel-91" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>470 <mxCell id="05-agents-panel-91" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="440" width="356" height="76" as="geometry"/></mxCell>
471 <mxCell id="05-agents-panel-92" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>471 <mxCell id="05-agents-panel-92" value="&lt;b&gt;5&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="450" width="24" height="24" as="geometry"/></mxCell>
472- <mxCell id="05-agents-panel-93" value="Journal" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="54.809999999999995" height="18.5" as="geometry"/></mxCell>472+ <mxCell id="05-agents-panel-93" value="Log" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="453.5" width="23.49" height="18.5" as="geometry"/></mxCell>
473- <mxCell id="05-agents-panel-94" value="stderr de l'agent, pour comprendre" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="236.64" height="17" as="geometry"/></mxCell>473+ <mxCell id="05-agents-panel-94" value="The agent's stderr, to understand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="478" width="229.67999999999998" height="17" as="geometry"/></mxCell>
474- <mxCell id="05-agents-panel-95" value="une erreur sans ouvrir un terminal." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="243.6" height="17" as="geometry"/></mxCell>474+ <mxCell id="05-agents-panel-95" value="an error without opening a terminal." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="496" width="250.55999999999997" height="17" as="geometry"/></mxCell>
475 </root>475 </root>
476 </mxGraphModel>476 </mxGraphModel>
477 </diagram>477 </diagram>
478- <diagram id="06-first-launch" name="6 · Premier lancement">478+ <diagram id="06-first-launch" name="6 · First launch">
479 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">479 <mxGraphModel dx="1660" dy="900" grid="1" gridSize="10" page="1" pageWidth="1660" pageHeight="900" math="0" shadow="0">
480 <root>480 <root>
481 <mxCell id="0"/>481 <mxCell id="0"/>
482 <mxCell id="1" parent="0"/>482 <mxCell id="1" parent="0"/>
483- <mxCell id="06-first-launch-title" value="&lt;b&gt;6 · Premier lancement&lt;/b&gt;&lt;br&gt;&lt;i&gt;Moins d'une minute pour savoir quoi faire, sans lire de documentation&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>483+ <mxCell id="06-first-launch-title" value="&lt;b&gt;6 · First launch&lt;/b&gt;&lt;br&gt;&lt;i&gt;Less than a minute to know what to do, without reading any documentation&lt;/i&gt;" style="text;html=1;align=left;verticalAlign=top;fontSize=16;fontColor=#1A237E;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="20" y="10" width="1200" height="60" as="geometry"/></mxCell>
484 <mxCell id="06-first-launch-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>484 <mxCell id="06-first-launch-1" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F6F6F7;strokeColor=#C9CBD1;strokeWidth=1;shadow=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="780" as="geometry"/></mxCell>
485 <mxCell id="06-first-launch-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>485 <mxCell id="06-first-launch-2" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="80" width="1240" height="36" as="geometry"/></mxCell>
486 <mxCell id="06-first-launch-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>486 <mxCell id="06-first-launch-3" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="104" width="1240" height="12" as="geometry"/></mxCell>
@@ -489,57 +489,57 @@
489 <mxCell id="06-first-launch-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>489 <mxCell id="06-first-launch-6" value="" style="ellipse;fillColor=#28C840;strokeColor=none;" vertex="1" parent="1"><mxGeometry x="74" y="92" width="12" height="12" as="geometry"/></mxCell>
490 <mxCell id="06-first-launch-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>490 <mxCell id="06-first-launch-7" value="Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#6B6F7A;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="606.07" y="90" width="67.86" height="18" as="geometry"/></mxCell>
491 <mxCell id="06-first-launch-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>491 <mxCell id="06-first-launch-8" value="" style="rounded=0;fillColor=#F0F1F3;strokeColor=none;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="20" y="834" width="1240" height="26" as="geometry"/></mxCell>
492- <mxCell id="06-first-launch-9" value="○ aucun agent configuré · config : créée à l'emplacement par défaut" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="446.89" height="16.5" as="geometry"/></mxCell>492+ <mxCell id="06-first-launch-9" value="○ no agent configured · config: created at the default location" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="32" y="839.5" width="420.21" height="16.5" as="geometry"/></mxCell>
493 <mxCell id="06-first-launch-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>493 <mxCell id="06-first-launch-10" value="⚙︎ Settings ⌘K" style="text;html=1;align=right;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=11.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1141.28" y="839.5" width="106.72" height="16.5" as="geometry"/></mxCell>
494- <mxCell id="06-first-launch-11" value="👋 Bienvenue dans Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=26;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="443.96000000000004" y="174" width="392.08" height="31" as="geometry"/></mxCell>494+ <mxCell id="06-first-launch-11" value="👋 Welcome to Sidekicks" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=26;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="474.12" y="174" width="331.76" height="31" as="geometry"/></mxCell>
495- <mxCell id="06-first-launch-12" value="Discutez avec des agents d'IA dans des channels, comme avec des collègues." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="318.1" y="219" width="643.8" height="20" as="geometry"/></mxCell>495+ <mxCell id="06-first-launch-12" value="Talk with AI agents in channels, as you would with colleagues." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=15;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="370.3" y="219" width="539.4" height="20" as="geometry"/></mxCell>
496 <mxCell id="06-first-launch-13" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="280" width="800" height="76" as="geometry"/></mxCell>496 <mxCell id="06-first-launch-13" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="280" width="800" height="76" as="geometry"/></mxCell>
497 <mxCell id="06-first-launch-14" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="300" width="36" height="36" as="geometry"/></mxCell>497 <mxCell id="06-first-launch-14" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="300" width="36" height="36" as="geometry"/></mxCell>
498 <mxCell id="06-first-launch-15" value="1" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="308" width="20" height="21" as="geometry"/></mxCell>498 <mxCell id="06-first-launch-15" value="1" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="308" width="20" height="21" as="geometry"/></mxCell>
499- <mxCell id="06-first-launch-16" value="Ajoutez un agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="296" width="148.48" height="21" as="geometry"/></mxCell>499+ <mxCell id="06-first-launch-16" value="Add an agent" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="296" width="111.35999999999999" height="21" as="geometry"/></mxCell>
500- <mxCell id="06-first-launch-17" value="Un assistant qui tourne sur votre ordinateur, par exemple avec mm." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="322.5" width="516.78" height="18.5" as="geometry"/></mxCell>500+ <mxCell id="06-first-launch-17" value="An assistant that runs on your computer, for example with mm." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="322.5" width="477.63" height="18.5" as="geometry"/></mxCell>
501 <mxCell id="06-first-launch-18" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="372" width="800" height="76" as="geometry"/></mxCell>501 <mxCell id="06-first-launch-18" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="372" width="800" height="76" as="geometry"/></mxCell>
502 <mxCell id="06-first-launch-19" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="392" width="36" height="36" as="geometry"/></mxCell>502 <mxCell id="06-first-launch-19" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="392" width="36" height="36" as="geometry"/></mxCell>
503 <mxCell id="06-first-launch-20" value="2" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="400" width="20" height="21" as="geometry"/></mxCell>503 <mxCell id="06-first-launch-20" value="2" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="400" width="20" height="21" as="geometry"/></mxCell>
504- <mxCell id="06-first-launch-21" value="Ouvrez un channel" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="388" width="157.76" height="21" as="geometry"/></mxCell>504+ <mxCell id="06-first-launch-21" value="Open a channel" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="388" width="129.92" height="21" as="geometry"/></mxCell>
505- <mxCell id="06-first-launch-22" value="#général est déjà prêt. Créez-en d'autres par sujet." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="414.5" width="407.15999999999997" height="18.5" as="geometry"/></mxCell>505+ <mxCell id="06-first-launch-22" value="#general is ready. Create others, one per topic." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="414.5" width="375.84" height="18.5" as="geometry"/></mxCell>
506 <mxCell id="06-first-launch-23" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="464" width="800" height="76" as="geometry"/></mxCell>506 <mxCell id="06-first-launch-23" value="" style="rounded=1;absoluteArcSize=1;arcSize=24;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="464" width="800" height="76" as="geometry"/></mxCell>
507 <mxCell id="06-first-launch-24" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="484" width="36" height="36" as="geometry"/></mxCell>507 <mxCell id="06-first-launch-24" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#3B6FE0;strokeColor=none;fontColor=#FFFFFF;fontSize=16;" vertex="1" parent="1"><mxGeometry x="262" y="484" width="36" height="36" as="geometry"/></mxCell>
508 <mxCell id="06-first-launch-25" value="3" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="492" width="20" height="21" as="geometry"/></mxCell>508 <mxCell id="06-first-launch-25" value="3" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="270.0" y="492" width="20" height="21" as="geometry"/></mxCell>
509- <mxCell id="06-first-launch-26" value="Appelez-le avec @" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="480" width="157.76" height="21" as="geometry"/></mxCell>509+ <mxCell id="06-first-launch-26" value="Call it with @" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=16;fontColor=#1D1F24;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="316" y="480" width="129.92" height="21" as="geometry"/></mxCell>
510- <mxCell id="06-first-launch-27" value="« @alice bonjour » : Alice rejoint le channel et vous répond." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="506.5" width="477.63" height="18.5" as="geometry"/></mxCell>510+ <mxCell id="06-first-launch-27" value="&quot;@alice hello&quot;: Alice joins the channel and answers you." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="316" y="506.5" width="438.47999999999996" height="18.5" as="geometry"/></mxCell>
511 <mxCell id="06-first-launch-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="420" y="570" width="240" height="40" as="geometry"/></mxCell>511 <mxCell id="06-first-launch-28" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#3B6FE0;strokeColor=#3B6FE0;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="420" y="570" width="240" height="40" as="geometry"/></mxCell>
512- <mxCell id="06-first-launch-29" value="+ Ajouter mon premier agent" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="438.21000000000004" y="582.0" width="203.57999999999998" height="18" as="geometry"/></mxCell>512+ <mxCell id="06-first-launch-29" value="+ Add my first agent" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#FFFFFF;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="464.6" y="582.0" width="150.79999999999998" height="18" as="geometry"/></mxCell>
513 <mxCell id="06-first-launch-30" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="680" y="570" width="200" height="40" as="geometry"/></mxCell>513 <mxCell id="06-first-launch-30" value="" style="rounded=1;absoluteArcSize=1;arcSize=12;fillColor=#FFFFFF;strokeColor=#E2E3E6;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="680" y="570" width="200" height="40" as="geometry"/></mxCell>
514- <mxCell id="06-first-launch-31" value="Ouvrir une configuration…" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="685.75" y="582.0" width="188.5" height="18" as="geometry"/></mxCell>514+ <mxCell id="06-first-launch-31" value="Open a configuration…" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="700.83" y="582.0" width="158.33999999999997" height="18" as="geometry"/></mxCell>
515- <mxCell id="06-first-launch-32" value="Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="305.92" y="638" width="668.16" height="17" as="geometry"/></mxCell>515+ <mxCell id="06-first-launch-32" value="Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#6B6F7A;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="295.48" y="638" width="689.04" height="17" as="geometry"/></mxCell>
516 <mxCell id="06-first-launch-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="680" width="800" height="70" as="geometry"/></mxCell>516 <mxCell id="06-first-launch-33" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#FDECEF;strokeColor=#D64561;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="240" y="680" width="800" height="70" as="geometry"/></mxCell>
517- <mxCell id="06-first-launch-34" value="La commande « mm » est introuvable." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="260" y="694" width="284.2" height="19" as="geometry"/></mxCell>517+ <mxCell id="06-first-launch-34" value="The command &quot;mm&quot; cannot be found." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=14;fontColor=#D64561;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="260" y="694" width="267.96" height="19" as="geometry"/></mxCell>
518- <mxCell id="06-first-launch-35" value="Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="260" y="719" width="618.28" height="18" as="geometry"/></mxCell>518+ <mxCell id="06-first-launch-35" value="Install it (link to the guide) or choose another program for this agent." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13;fontColor=#1D1F24;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="260" y="719" width="542.88" height="18" as="geometry"/></mxCell>
519 <mxCell id="06-first-launch-36" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="300" width="24" height="24" as="geometry"/></mxCell>519 <mxCell id="06-first-launch-36" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="300" width="24" height="24" as="geometry"/></mxCell>
520 <mxCell id="06-first-launch-37" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="880" y="578" width="24" height="24" as="geometry"/></mxCell>520 <mxCell id="06-first-launch-37" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="880" y="578" width="24" height="24" as="geometry"/></mxCell>
521 <mxCell id="06-first-launch-38" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1018.0" y="634" width="24" height="24" as="geometry"/></mxCell>521 <mxCell id="06-first-launch-38" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1018.0" y="634" width="24" height="24" as="geometry"/></mxCell>
522 <mxCell id="06-first-launch-39" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="688" width="24" height="24" as="geometry"/></mxCell>522 <mxCell id="06-first-launch-39" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1040" y="688" width="24" height="24" as="geometry"/></mxCell>
523 <mxCell id="06-first-launch-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>523 <mxCell id="06-first-launch-40" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="80" width="356" height="76" as="geometry"/></mxCell>
524 <mxCell id="06-first-launch-41" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>524 <mxCell id="06-first-launch-41" value="&lt;b&gt;1&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="90" width="24" height="24" as="geometry"/></mxCell>
525- <mxCell id="06-first-launch-42" value="Trois étapes, pas plus" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="172.26" height="18.5" as="geometry"/></mxCell>525+ <mxCell id="06-first-launch-42" value="Three steps, no more" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="93.5" width="156.6" height="18.5" as="geometry"/></mxCell>
526- <mxCell id="06-first-launch-43" value="L'état vide explique le modèle mental :" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="271.44" height="17" as="geometry"/></mxCell>526+ <mxCell id="06-first-launch-43" value="The empty state explains the mental model:" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="118" width="292.32" height="17" as="geometry"/></mxCell>
527 <mxCell id="06-first-launch-44" value="agents, channels, @ (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="180.95999999999998" height="17" as="geometry"/></mxCell>527 <mxCell id="06-first-launch-44" value="agents, channels, @ (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="136" width="180.95999999999998" height="17" as="geometry"/></mxCell>
528 <mxCell id="06-first-launch-45" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>528 <mxCell id="06-first-launch-45" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="170" width="356" height="76" as="geometry"/></mxCell>
529 <mxCell id="06-first-launch-46" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>529 <mxCell id="06-first-launch-46" value="&lt;b&gt;2&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="180" width="24" height="24" as="geometry"/></mxCell>
530- <mxCell id="06-first-launch-47" value="Un assistant plutôt que du YAML" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="242.73" height="18.5" as="geometry"/></mxCell>530+ <mxCell id="06-first-launch-47" value="An assistant rather than YAML" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="183.5" width="227.07" height="18.5" as="geometry"/></mxCell>
531- <mxCell id="06-first-launch-48" value="Le formulaire écrit le YAML à la place de" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="285.35999999999996" height="17" as="geometry"/></mxCell>531+ <mxCell id="06-first-launch-48" value="The form writes the YAML for the user;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="208" width="264.47999999999996" height="17" as="geometry"/></mxCell>
532- <mxCell id="06-first-launch-49" value="l'utilisateur ; Settings reste accessible." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="292.32" height="17" as="geometry"/></mxCell>532+ <mxCell id="06-first-launch-49" value="Settings stays at hand." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="226" width="160.07999999999998" height="17" as="geometry"/></mxCell>
533 <mxCell id="06-first-launch-50" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>533 <mxCell id="06-first-launch-50" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="260" width="356" height="76" as="geometry"/></mxCell>
534 <mxCell id="06-first-launch-51" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>534 <mxCell id="06-first-launch-51" value="&lt;b&gt;3&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="270" width="24" height="24" as="geometry"/></mxCell>
535- <mxCell id="06-first-launch-52" value="Rien à créer à la main" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="172.26" height="18.5" as="geometry"/></mxCell>535+ <mxCell id="06-first-launch-52" value="Nothing to create by hand" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="273.5" width="195.75" height="18.5" as="geometry"/></mxCell>
536- <mxCell id="06-first-launch-53" value="Le fichier par défaut existe déjà, avec des" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="299.28" height="17" as="geometry"/></mxCell>536+ <mxCell id="06-first-launch-53" value="The default file already exists, with" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="298" width="257.52" height="17" as="geometry"/></mxCell>
537- <mxCell id="06-first-launch-54" value="commentaires (#9)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="125.27999999999999" height="17" as="geometry"/></mxCell>537+ <mxCell id="06-first-launch-54" value="comments (#9)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="316" width="97.44" height="17" as="geometry"/></mxCell>
538 <mxCell id="06-first-launch-55" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>538 <mxCell id="06-first-launch-55" value="" style="rounded=1;absoluteArcSize=1;arcSize=20;fillColor=#F3E5F5;strokeColor=#6A1B9A;strokeWidth=1;html=1;" vertex="1" parent="1"><mxGeometry x="1284" y="350" width="356" height="76" as="geometry"/></mxCell>
539 <mxCell id="06-first-launch-56" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>539 <mxCell id="06-first-launch-56" value="&lt;b&gt;4&lt;/b&gt;" style="ellipse;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=13;" vertex="1" parent="1"><mxGeometry x="1294" y="360" width="24" height="24" as="geometry"/></mxCell>
540- <mxCell id="06-first-launch-57" value="Erreurs humaines" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="125.27999999999999" height="18.5" as="geometry"/></mxCell>540+ <mxCell id="06-first-launch-57" value="Human errors" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=13.5;fontColor=#4A148C;fontStyle=1;" vertex="1" parent="1"><mxGeometry x="1328" y="363.5" width="93.96" height="18.5" as="geometry"/></mxCell>
541- <mxCell id="06-first-launch-58" value="Dire ce qui ne va pas et quoi faire ;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="257.52" height="17" as="geometry"/></mxCell>541+ <mxCell id="06-first-launch-58" value="Say what is wrong and what to do;" style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="388" width="229.67999999999998" height="17" as="geometry"/></mxCell>
542- <mxCell id="06-first-launch-59" value="jamais de trace technique (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="222.71999999999997" height="17" as="geometry"/></mxCell>542+ <mxCell id="06-first-launch-59" value="never a technical trace (#33)." style="text;html=1;align=left;verticalAlign=middle;strokeColor=none;fillColor=none;spacing=0;fontSize=12;fontColor=#4A148C;fontStyle=0;" vertex="1" parent="1"><mxGeometry x="1300" y="406" width="208.79999999999998" height="17" as="geometry"/></mxCell>
543 </root>543 </root>
544 </mxGraphModel>544 </mxGraphModel>
545 </diagram>545 </diagram>
modified docs/design/wireframes.py +97 -97
@@ -98,7 +98,7 @@ class Screen:
9898
9999 # -- common pieces of the window -------------------------------------------------
100100
101- def window(self, status="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml"):
101+ def window(self, status="● 2 of 2 agents running · config: ~/agents/team.yaml"):
102102 """The window chrome: title bar, left dock, status bar."""
103103 self.rect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, BG, "#C9CBD1", rx=12, shadow=True)
104104 self.rect(FRAME_X, FRAME_Y, FRAME_W, 36, PANEL2, None, rx=12)
@@ -111,12 +111,12 @@ class Screen:
111111 self.text(FRAME_X + 12, FRAME_Y + FRAME_H - 9, status, 11.5, MUTED)
112112 self.text(FRAME_X + FRAME_W - 12, FRAME_Y + FRAME_H - 9, "⚙︎ Settings ⌘K", 11.5, MUTED, anchor="end")
113113
114- def left_dock(self, active="général", agents=(("alice", "👩 Alice", OK), ("bob", "🧔 Bob", OK), ("riker", "🧑‍🚀 Riker", MUTED), ("garfield", "🐮 Garfield", WARN))):
114+ def left_dock(self, active="general", agents=(("alice", "👩 Alice", OK), ("bob", "🧔 Bob", OK), ("riker", "🧑‍🚀 Riker", MUTED), ("garfield", "🐮 Garfield", WARN))):
115115 x, y = FRAME_X, FRAME_Y + 36
116116 self.rect(x, y, 230, FRAME_H - 62, PANEL, BORDER)
117117 self.text(x + 16, y + 28, "CHANNELS", 11, MUTED, bold=True)
118118 self.text(x + 214, y + 28, "+", 16, MUTED, bold=True, anchor="end")
119- for index, name in enumerate(("général", "traductions", "code")):
119+ for index, name in enumerate(("general", "translations", "code")):
120120 top = y + 42 + index * 30
121121 if name == active:
122122 self.rect(x + 8, top, 214, 26, PANEL2, None, rx=6)
@@ -136,7 +136,7 @@ class Screen:
136136 self.lines(x, y + 22, lines, 13.5)
137137 return y + 22 + 20 * len(lines) + 14
138138
139- def composer(self, x, y, w, value, hint="Entrée pour envoyer · Maj+Entrée nouvelle ligne"):
139+ def composer(self, x, y, w, value, hint="Enter to send · Shift+Enter for a new line"):
140140 self.rect(x, y, w, 64, PANEL, ACCENT, rx=10, width=1.5)
141141 self.text(x + 14, y + 26, value, 14)
142142 self.text(x + 14, y + 50, hint, 11, MUTED)
@@ -163,98 +163,98 @@ CENTER_W = FRAME_W - 231
163163
164164
165165 def screen_layout():
166- s = Screen("01-layout", "1 · Layout global", "Docks façon Zed, mais seul l'essentiel est visible par défaut")
166+ s = Screen("01-layout", "1 · Overall layout", "Zed-like docks, but only the essentials are visible by default")
167167 s.window()
168168 s.left_dock()
169169 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W - 300, 44, PANEL, BORDER)
170- s.text(CENTER_X + 16, FRAME_Y + 64, "# général", 15, bold=True)
170+ s.text(CENTER_X + 16, FRAME_Y + 64, "# general", 15, bold=True)
171171 s.text(CENTER_X + CENTER_W - 316, FRAME_Y + 64, "👩 Alice · 🧔 Bob", 13, MUTED, anchor="end")
172- y = s.message(CENTER_X + 24, FRAME_Y + 116, "Vous", TEXT, ["@alice résume-moi ce que fait ACP en deux phrases"])
173- y = s.message(CENTER_X + 24, y, "👩 Alice", AGENT_COLOURS["alice"], ["ACP est un protocole qui permet à une application de dialoguer avec", "n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique."])
174- s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 136, CENTER_W - 340, "Écrivez un message… (@ pour un agent, / pour une commande)")
172+ y = s.message(CENTER_X + 24, FRAME_Y + 116, "You", TEXT, ["@alice sum up what ACP does in two sentences"])
173+ y = s.message(CENTER_X + 24, y, "👩 Alice", AGENT_COLOURS["alice"], ["ACP is a protocol that lets an application talk with any AI agent.", "Sidekicks uses it to stay agent-agnostic."])
174+ s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 136, CENTER_W - 340, "Write a message… (@ for an agent, / for a command)")
175175 # Right dock (Settings), shown here opened.
176176 s.rect(FRAME_X + FRAME_W - 300, FRAME_Y + 36, 300, FRAME_H - 62, PANEL, BORDER)
177- s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 64, "⚙︎ Settings — equipe.yaml", 13, MUTED, bold=True)
177+ s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 64, "⚙︎ Settings — team.yaml", 13, MUTED, bold=True)
178178 s.rect(FRAME_X + FRAME_W - 290, FRAME_Y + 80, 280, 300, CODE, BORDER, rx=6)
179- s.lines(FRAME_X + FRAME_W - 280, FRAME_Y + 104, ["version: 1", "agents:", " - name: alice", " title: Alice", " command: mm", " args: [-acp, ...]", "channels:", " - name: général", " agents: [alice, bob]"], 12, TEXT, gap=19, mono=True)
180- s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 410, "✓ Configuration valide", 12.5, OK, bold=True)
181- s.button(FRAME_X + FRAME_W - 290, FRAME_Y + 428, 120, "Enregistrer", primary=True)
179+ s.lines(FRAME_X + FRAME_W - 280, FRAME_Y + 104, ["version: 1", "agents:", " - name: alice", " title: Alice", " command: mm", " args: [-acp, ...]", "channels:", " - name: general", " agents: [alice, bob]"], 12, TEXT, gap=19, mono=True)
180+ s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 410, "✓ Valid configuration", 12.5, OK, bold=True)
181+ s.button(FRAME_X + FRAME_W - 290, FRAME_Y + 428, 120, "Save", primary=True)
182182 # Collapsed bottom dock.
183183 s.rect(CENTER_X, FRAME_Y + FRAME_H - 26 - 30, CENTER_W - 300, 30, PANEL2, BORDER)
184- s.text(CENTER_X + 14, FRAME_Y + FRAME_H - 36, "▸ Terminal · Fichiers (repliés)", 12, MUTED)
184+ s.text(CENTER_X + 14, FRAME_Y + FRAME_H - 36, "▸ Terminal · Files (folded)", 12, MUTED)
185185 s.badge(FRAME_X + 200, FRAME_Y + 62, 1)
186186 s.badge(CENTER_X + 120, FRAME_Y + 160, 2)
187187 s.badge(FRAME_X + FRAME_W - 30, FRAME_Y + 60, 3)
188188 s.badge(CENTER_X + 330, FRAME_Y + FRAME_H - 41, 4)
189189 s.badge(FRAME_X + 420, FRAME_Y + FRAME_H - 13, 5)
190190 s.notes([
191- (1, "Dock gauche : channels et agents", ["Toujours visible. La pastille donne l'état de", "l'agent : vert prêt, orange occupé, gris arrêté."]),
192- (2, "Centre : la conversation", ["Le seul panel indispensable. Pleine largeur", "quand les docks sont repliés."]),
193- (3, "Dock droit : Settings", ["Éditeur YAML (CodeMirror) avec validation", "en direct. Fermé par défaut ; s'ouvre", "par ⚙︎ ou ⌘,."]),
194- (4, "Dock bas : terminal et fichiers", ["Replié par défaut : utile aux curieux,", "jamais imposé au citizen user."]),
195- (5, "Barre d'état", ["Rappelle la limite d'agents simultanés", "(mémoire) et la configuration ouverte."]),
191+ (1, "Left dock: channels and agents", ["Always visible. The dot gives the agent's", "state: green ready, orange busy, grey stopped."]),
192+ (2, "Centre: the conversation", ["The only panel you cannot do without.", "Full width when the docks are folded."]),
193+ (3, "Right dock: Settings", ["YAML editor (CodeMirror) with live", "validation. Closed by default; opens", "with ⚙︎ or ⌘,."]),
194+ (4, "Bottom dock: terminal and files", ["Folded by default: there for the curious,", "never forced on the citizen user."]),
195+ (5, "Status bar", ["Shows the limit of agents running at once", "(memory) and the open configuration."]),
196196 ])
197197 return s
198198
199199
200200 def screen_conversation():
201- s = Screen("02-conversation", "2 · Conversation multi-agents", "Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter")
201+ s = Screen("02-conversation", "2 · Multi-agent conversation", "Each agent has its colour; tools are folded; you can always stop")
202202 s.window()
203203 s.left_dock()
204204 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)
205- s.text(CENTER_X + 16, FRAME_Y + 64, "# général", 15, bold=True)
205+ s.text(CENTER_X + 16, FRAME_Y + 64, "# general", 15, bold=True)
206206 s.text(CENTER_X + CENTER_W - 16, FRAME_Y + 64, "👩 Alice · 🧔 Bob · 🧑‍🚀 Riker", 13, MUTED, anchor="end")
207207 x = CENTER_X + 24
208- y = s.message(x, FRAME_Y + 116, "", "", ["— Riker a rejoint le channel —"], system=True)
209- y = s.message(x, y + 6, "Vous", TEXT, ["@alice propose un nom pour un script qui sauvegarde mes photos"])
210- y = s.message(x, y, "👩 Alice", AGENT_COLOURS["alice"], ["Je propose « photo-backup.sh » : court, explicite, et l'extension dit que", "c'est un script shell."])
211- y = s.message(x, y, "Vous", TEXT, ["@bob tu valides ? @riker crée le fichier vide"])
212- y = s.message(x, y, "🧔 Bob", AGENT_COLOURS["bob"], ["Bon choix. Alice a raison : un nom en minuscules, sans espace."])
208+ y = s.message(x, FRAME_Y + 116, "", "", ["— Riker joined the channel —"], system=True)
209+ y = s.message(x, y + 6, "You", TEXT, ["@alice suggest a name for a script that backs up my photos"])
210+ y = s.message(x, y, "👩 Alice", AGENT_COLOURS["alice"], ["I suggest \"photo-backup.sh\": short, clear, and the extension says", "it is a shell script."])
211+ y = s.message(x, y, "You", TEXT, ["@bob do you agree? @riker create the empty file"])
212+ y = s.message(x, y, "🧔 Bob", AGENT_COLOURS["bob"], ["Good choice. Alice is right: a lowercase name, no spaces."])
213213 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)
214- s.text(x + 90, y, "travaille…", 12.5, MUTED, italic=True)
214+ s.text(x + 90, y, "working…", 12.5, MUTED, italic=True)
215215 s.rect(x, y + 12, 560, 40, PANEL2, BORDER, rx=8)
216- s.text(x + 14, y + 37, "▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé", 12.5, TEXT)
216+ s.text(x + 14, y + 37, "▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done", 12.5, TEXT)
217217 s.rect(x, y + 60, 560, 40, PANEL2, BORDER, rx=8)
218- s.text(x + 14, y + 85, "▸ 🛠 bash : ls -la … en cours", 12.5, TEXT)
218+ s.text(x + 14, y + 85, "▸ 🛠 bash: ls -la … running", 12.5, TEXT)
219219 s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 106, CENTER_W - 160, "")
220- s.button(CENTER_X + CENTER_W - 128, FRAME_Y + FRAME_H - 90, 108, "■ Arrêter", danger=True)
220+ s.button(CENTER_X + CENTER_W - 128, FRAME_Y + FRAME_H - 90, 108, "■ Stop", danger=True)
221221 s.rect(CENTER_X + CENTER_W - 220, FRAME_Y + 96, 200, 30, PANEL2, BORDER, rx=15)
222- s.text(CENTER_X + CENTER_W - 120, FRAME_Y + 116, "contexte Riker : 41 %", 11.5, MUTED, anchor="middle")
222+ s.text(CENTER_X + CENTER_W - 120, FRAME_Y + 116, "Riker's context: 41%", 11.5, MUTED, anchor="middle")
223223 s.badge(x + 300, FRAME_Y + 111, 1)
224224 s.badge(x + 80, FRAME_Y + 194, 2)
225225 s.badge(x + 580, y + 32, 3)
226226 s.badge(CENTER_X + CENTER_W - 140, FRAME_Y + FRAME_H - 90, 4)
227227 s.badge(CENTER_X + CENTER_W - 232, FRAME_Y + 111, 5)
228228 s.notes([
229- (1, "Arrivée visible", ["La 1re mention invite l'agent : un message", "système le dit. On sait qui « écoute »."]),
230- (2, "Une couleur par agent", ["Avatar + nom colorés, réponses en Markdown", "avec code coloré (ADR 0002)."]),
231- (3, "Outils repliés", ["Une ligne par tool call : titre lisible et", "statut. Déplier montre la commande, le", "diff ou la sortie."]),
232- (4, "Toujours un bouton Arrêter", ["Envoie session/cancel aux agents qui", "répondent dans ce channel (ADR 0001)."]),
233- (5, "Jauge de contexte", ["usage_update : prévient avant que la", "conversation ne dépasse la mémoire de", "l'agent."]),
229+ (1, "A visible arrival", ["The first mention invites the agent: a system", "message says so. You know who is listening."]),
230+ (2, "One colour per agent", ["Coloured avatar and name, answers in", "Markdown with highlighted code (ADR 0002)."]),
231+ (3, "Folded tools", ["One line per tool call: a readable title", "and its status. Unfolding shows the", "command, the diff or the output."]),
232+ (4, "Always a Stop button", ["Sends session/cancel to the agents", "answering in this channel (ADR 0001)."]),
233+ (5, "Context gauge", ["usage_update: warns before the", "conversation outgrows the agent's", "memory."]),
234234 ])
235235 return s
236236
237237
238238 def screen_popups():
239- s = Screen("03-mentions-commands", "3 · Invoquer un agent (@) et une commande (/)", "Tab choisit la suggestion ; continuer à écrire filtre la liste")
239+ s = Screen("03-mentions-commands", "3 · Calling an agent (@) and a command (/)", "Tab picks the suggestion; typing on filters the list")
240240 s.window()
241241 s.left_dock()
242242 x, w = CENTER_X + 20, CENTER_W - 40
243243 # @ popup (top half).
244- s.text(x, FRAME_Y + 70, "Mention d'un agent", 14, MUTED, bold=True)
244+ s.text(x, FRAME_Y + 70, "Mentioning an agent", 14, MUTED, bold=True)
245245 s.rect(x, FRAME_Y + 84, 420, 150, PANEL, BORDER, rx=10, shadow=True)
246- rows = [("🐮 garfield", "Garfield (traducteur) — traduit FR ⇄ EN", True), ("🧑‍🚀 riker", "Riker (outils) — commandes et fichiers", False), ("👩 alice", "Alice (assistante) — questions simples", False)]
246+ rows = [("🐮 garfield", "Garfield (translator) — translates FR ⇄ EN", True), ("🧑‍🚀 riker", "Riker (tools) — commands and files", False), ("👩 alice", "Alice (assistant) — simple questions", False)]
247247 for index, (name, description, selected) in enumerate(rows):
248248 top = FRAME_Y + 92 + index * 44
249249 if selected:
250250 s.rect(x + 6, top, 408, 40, "#E8EFFD", None, rx=6)
251251 s.text(x + 18, top + 18, name, 13.5, TEXT, bold=True)
252252 s.text(x + 18, top + 34, description, 11.5, MUTED)
253- s.composer(x, FRAME_Y + 244, w, "@ga", "Tab : @garfield · ↑↓ naviguer · Échap fermer")
253+ s.composer(x, FRAME_Y + 244, w, "@ga", "Tab: @garfield · ↑↓ move · Esc close")
254254 # / popup (bottom half).
255- s.text(x, FRAME_Y + 360, "Commande d'un agent (plusieurs agents dans le channel)", 14, MUTED, bold=True)
255+ s.text(x, FRAME_Y + 360, "An agent's command (several agents in the channel)", 14, MUTED, bold=True)
256256 s.rect(x, FRAME_Y + 374, 520, 196, PANEL, BORDER, rx=10, shadow=True)
257- commands = [("/garfield:idioms", "Expressions idiomatiques et faux amis", "[texte]", True), ("/garfield:styles", "Plusieurs versions d'une traduction", "[texte]", False), ("/garfield:email-letter", "E-mails et lettres : formules, tu ou vous", "[texte]", False), ("/alice:compact", "Résumer l'historique maintenant", "", False)]
257+ commands = [("/garfield:idioms", "Idioms and false friends", "[text]", True), ("/garfield:styles", "Several versions of a translation", "[text]", False), ("/garfield:email-letter", "E-mails and letters: greetings, tu or vous", "[text]", False), ("/alice:compact", "Summarise the history now", "", False)]
258258 for index, (name, description, hint, selected) in enumerate(commands):
259259 top = FRAME_Y + 382 + index * 46
260260 if selected:
@@ -262,90 +262,90 @@ def screen_popups():
262262 s.text(x + 18, top + 19, name, 13.5, TEXT, bold=True, mono=True)
263263 s.text(x + 500, top + 19, hint, 11.5, MUTED, anchor="end", mono=True)
264264 s.text(x + 18, top + 36, description, 11.5, MUTED)
265- s.composer(x, FRAME_Y + 580, w, "/gar", "Tab : /garfield:idioms · le texte après la commande est son argument")
265+ s.composer(x, FRAME_Y + 580, w, "/gar", "Tab: /garfield:idioms · the text after the command is its argument")
266266 s.badge(x + 434, FRAME_Y + 100, 1)
267267 s.badge(x + 300, FRAME_Y + 276, 2)
268268 s.badge(x + 534, FRAME_Y + 390, 3)
269269 s.badge(x + 534, FRAME_Y + 530, 4)
270270 s.badge(x + w - 20, FRAME_Y + 612, 5)
271271 s.notes([
272- (1, "@ liste les agents de la config", ["Nom technique + titre + description.", "Filtrage flou sur nom et titre (« gar »)."]),
273- (2, "Tab sélectionne, sinon on écrit", ["Comportement demandé dans le draft (#1).", "Échap ferme sans rien insérer."]),
274- (3, "/agent:commande en multi-agents", ["Avec un seul agent : /commande. Sidekicks", "envoie « /idioms … » au seul Garfield."]),
275- (4, "Skills et commandes intégrées", ["mm annonce compact, new, unload et ses", "skills (available_commands_update)."]),
276- (5, "Indice d'argument", ["input.hint de la commande ACP, affiché", "tant que l'argument n'est pas saisi."]),
272+ (1, "@ lists the configured agents", ["Technical name + title + description.", "Fuzzy filter on name and title (\"gar\")."]),
273+ (2, "Tab picks, otherwise you type", ["Behaviour asked for in the draft (#1).", "Esc closes without inserting anything."]),
274+ (3, "/agent:command with several agents", ["With one agent: /command. Sidekicks", "sends \"/idioms …\" to Garfield only."]),
275+ (4, "Skills and built-in commands", ["mm announces compact, new, unload and", "its skills (available_commands_update)."]),
276+ (5, "Argument hint", ["The ACP command's input.hint, shown", "until the argument is typed."]),
277277 ])
278278 return s
279279
280280
281281 def screen_permission():
282- s = Screen("04-permission", "4 · Demande de permission", "En langage clair, sur la conversation concernée, avec une sortie de secours")
282+ s = Screen("04-permission", "4 · Permission request", "In plain words, on the conversation concerned, with a way out")
283283 s.window()
284284 s.left_dock(active="code")
285285 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)
286286 s.text(CENTER_X + 16, FRAME_Y + 64, "# code", 15, bold=True)
287287 x = CENTER_X + 24
288- y = s.message(x, FRAME_Y + 116, "Vous", TEXT, ["@riker supprime les fichiers temporaires du dossier"])
288+ y = s.message(x, FRAME_Y + 116, "You", TEXT, ["@riker delete the temporary files in the folder"])
289289 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)
290- s.text(x + 90, y, "attend votre réponse", 12.5, WARN, italic=True)
290+ s.text(x + 90, y, "waiting for your answer", 12.5, WARN, italic=True)
291291 # The dialog, attached to the conversation (not a global modal).
292292 dx, dy, dw = x, y + 18, 720
293293 s.rect(dx, dy, dw, 330, PANEL, WARN, rx=12, width=2, shadow=True)
294- s.text(dx + 20, dy + 34, "🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur", 16, TEXT, bold=True)
294+ s.text(dx + 20, dy + 34, "🧑‍🚀 Riker wants to run a command on your computer", 16, TEXT, bold=True)
295295 s.rect(dx + 20, dy + 52, dw - 40, 40, CODE, BORDER, rx=6)
296296 s.text(dx + 34, dy + 77, "rm -f *.tmp", 14, TEXT, mono=True)
297- s.lines(dx + 20, dy + 118, ["Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp", "dans agents/riker . Ils ne passent pas par la corbeille."], 13, TEXT, gap=20)
298- s.text(dx + 20, dy + 168, "⚠ Action irréversible", 13, DANGER, bold=True)
299- s.button(dx + 20, dy + 190, 170, "Autoriser une fois", primary=True)
300- s.button(dx + 200, dy + 190, 300, "Toujours autoriser bash ici")
301- s.button(dx + 20, dy + 234, 170, "Refuser")
302- s.button(dx + 200, dy + 234, 300, "Ne plus jamais proposer bash ici")
303- s.button(dx + 520, dy + 234, 180, "■ Arrêter Riker", danger=True)
304- s.text(dx + 20, dy + 300, "▸ Détails techniques (tool call bash, rawInput)", 12, MUTED)
297+ s.lines(dx + 20, dy + 118, ["What it does: permanently deletes the files ending in .tmp", "in agents/riker . They do not go to the trash."], 13, TEXT, gap=20)
298+ s.text(dx + 20, dy + 168, "⚠ Cannot be undone", 13, DANGER, bold=True)
299+ s.button(dx + 20, dy + 190, 170, "Allow once", primary=True)
300+ s.button(dx + 200, dy + 190, 300, "Always allow bash here")
301+ s.button(dx + 20, dy + 234, 170, "Reject")
302+ s.button(dx + 200, dy + 234, 300, "Never offer bash here again")
303+ s.button(dx + 520, dy + 234, 180, "■ Stop Riker", danger=True)
304+ s.text(dx + 20, dy + 300, "▸ Technical details (bash tool call, rawInput)", 12, MUTED)
305305 s.badge(dx + dw - 16, dy + 30, 1)
306306 s.badge(dx + 680, dy + 128, 2)
307307 s.badge(dx + 512, dy + 206, 3)
308308 s.badge(dx + 712, dy + 250, 4)
309309 s.badge(dx + 380, dy + 296, 5)
310310 s.notes([
311- (1, "Attaché à la conversation", ["Bloque seulement la session de Riker ; les", "autres channels continuent (#17)."]),
312- (2, "Expliquer, pas montrer du JSON", ["Titre ACP + kind + rawInput traduits en", "phrase. Les actions destructives sont", "signalées."]),
313- (3, "Les 4 options ACP, par ids", ["allow · allow_always · reject · never.", "« Ne plus jamais » est mis en avant au", "lieu de « Refuser une fois »."]),
314- (4, "Toujours une sortie de secours", ["Spike ACP : après un refus, un petit modèle", "a réessayé 40 fois. Arrêter = session/cancel."]),
315- (5, "Détails pour les curieux", ["Repliés par défaut."]),
311+ (1, "Attached to the conversation", ["Blocks only Riker's session; the other", "channels go on (#17)."]),
312+ (2, "Explain, do not show JSON", ["ACP title + kind + rawInput turned into", "a sentence. Destructive actions are", "flagged."]),
313+ (3, "The 4 ACP options, by id", ["allow · allow_always · reject · never.", "\"Never again\" is put forward rather", "than \"Reject once\"."]),
314+ (4, "Always a way out", ["ACP spike: after a rejection, a small model", "retried 40 times. Stop = session/cancel."]),
315+ (5, "Details for the curious", ["Folded by default."]),
316316 ])
317317 return s
318318
319319
320320 def screen_agents():
321- s = Screen("05-agents-panel", "5 · Panel Agents : fichiers, éditeur, terminal", "Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx")
321+ s = Screen("05-agents-panel", "5 · Agents panel: files, editor, terminal", "The agent's workspace, whether it runs locally, in a container or in an sbx sandbox")
322322 s.window()
323323 s.left_dock(active="")
324324 x, y = CENTER_X, FRAME_Y + 36
325325 s.rect(x, y, CENTER_W, 56, PANEL, BORDER)
326- s.text(x + 16, y + 26, "🐮 Garfield (traducteur)", 16, bold=True)
327- s.text(x + 16, y + 46, "● occupé · local · agents/garfield · gemma-4-E4B", 12, WARN)
328- s.button(x + CENTER_W - 250, y + 12, 110, "Redémarrer")
329- s.button(x + CENTER_W - 130, y + 12, 110, "Arrêter", danger=True)
330- tabs = ["Fichiers", "Terminal", "Journal"]
326+ s.text(x + 16, y + 26, "🐮 Garfield (translator)", 16, bold=True)
327+ s.text(x + 16, y + 46, "● busy · local · agents/garfield · gemma-4-E4B", 12, WARN)
328+ s.button(x + CENTER_W - 250, y + 12, 110, "Restart")
329+ s.button(x + CENTER_W - 130, y + 12, 110, "Stop", danger=True)
330+ tabs = ["Files", "Terminal", "Log"]
331331 for index, tab in enumerate(tabs):
332332 s.text(x + 24 + index * 100, y + 82, tab, 13.5, ACCENT if index == 0 else MUTED, bold=index == 0)
333333 s.rect(x + 16, y + 90, 70, 2, ACCENT, None)
334334 # Tree.
335335 s.rect(x, y + 100, 260, 420, PANEL, BORDER)
336- tree = ["▾ garfield", " AGENTS.md", " README.md", " agent.yaml", " ▾ skills", " ▸ email-letter", " ▸ idioms", " ▸ styles", " ▸ technical-text", " traduction.md"]
336+ tree = ["▾ garfield", " AGENTS.md", " README.md", " agent.yaml", " ▾ skills", " ▸ email-letter", " ▸ idioms", " ▸ styles", " ▸ technical-text", " translation.md"]
337337 for index, item in enumerate(tree):
338338 if item.strip() == "AGENTS.md":
339339 s.rect(x + 6, y + 112 + index * 26, 248, 24, "#E8EFFD", None, rx=4)
340340 s.text(x + 14, y + 129 + index * 26, item, 12.5, TEXT, mono=True)
341- s.text(x + 14, y + 400, "+ Nouveau fichier", 12.5, ACCENT)
341+ s.text(x + 14, y + 400, "+ New file", 12.5, ACCENT)
342342 # Editor.
343343 ex = x + 270
344344 s.rect(ex, y + 100, CENTER_W - 280, 420, CODE, BORDER)
345345 s.text(ex + 14, y + 124, "AGENTS.md", 13, MUTED, bold=True)
346- s.lines(ex + 14, y + 154, ["# Style de traduction", "", "- Registre : celui du texte source.", "- Typographie française : espaces insécables", " avant ; : ! ? et entre « guillemets ».", "- Ne jamais traduire le code ni les noms propres."], 12.5, TEXT, gap=22, mono=True)
347- s.button(ex + CENTER_W - 280 - 240, y + 474, 110, "Enregistrer", primary=True)
348- s.button(ex + CENTER_W - 280 - 120, y + 474, 110, "Supprimer", danger=True)
346+ s.lines(ex + 14, y + 154, ["# Translation style", "", "- Register: the one of the source text.", "- French typography: non-breaking spaces", " before ; : ! ? and inside « quotes ».", "- Never translate code or proper names."], 12.5, TEXT, gap=22, mono=True)
347+ s.button(ex + CENTER_W - 280 - 240, y + 474, 110, "Save", primary=True)
348+ s.button(ex + CENTER_W - 280 - 120, y + 474, 110, "Delete", danger=True)
349349 # Terminal (bottom).
350350 s.rect(x, y + 530, CENTER_W, FRAME_H - 26 - 36 - 530, "#111317", BORDER)
351351 s.lines(x + 14, y + 556, ["garfield $ ls skills", "email-letter idioms styles technical-text", "garfield $ █"], 12.5, "#D7DAE0", gap=20, mono=True)
@@ -355,24 +355,24 @@ def screen_agents():
355355 s.badge(x + CENTER_W - 20, y + 552, 4)
356356 s.badge(x + 330, y + 82, 5)
357357 s.notes([
358- (1, "État et emplacement de l'agent", ["Statut temps réel (#11), runtime (local,", "container, sbx), dossier et modèle."]),
359- (2, "Arbre du workspace", ["Chargement paresseux, rafraîchi quand", "l'agent modifie un fichier (#29)."]),
360- (3, "Éditer, supprimer avec confirmation", ["Même éditeur que Settings. Conflit signalé", "si l'agent modifie le fichier (#30)."]),
361- (4, "Terminal sur le workspace", ["PTY local, docker exec ou sbx selon le", "runtime (#28, #31). Replié par défaut."]),
362- (5, "Journal", ["stderr de l'agent, pour comprendre", "une erreur sans ouvrir un terminal."]),
358+ (1, "The agent's state and location", ["Live status (#11), runtime (local,", "container, sbx), folder and model."]),
359+ (2, "Workspace tree", ["Loaded lazily, refreshed when the", "agent changes a file (#29)."]),
360+ (3, "Edit, delete with confirmation", ["Same editor as Settings. A conflict is", "flagged if the agent changes the file (#30)."]),
361+ (4, "Terminal in the workspace", ["Local PTY, docker exec or sbx depending on", "the runtime (#28, #31). Folded by default."]),
362+ (5, "Log", ["The agent's stderr, to understand", "an error without opening a terminal."]),
363363 ])
364364 return s
365365
366366
367367 def screen_first_launch():
368- s = Screen("06-first-launch", "6 · Premier lancement", "Moins d'une minute pour savoir quoi faire, sans lire de documentation")
369- s.window(status="○ aucun agent configuré · config : créée à l'emplacement par défaut")
368+ s = Screen("06-first-launch", "6 · First launch", "Less than a minute to know what to do, without reading any documentation")
369+ s.window(status="○ no agent configured · config: created at the default location")
370370 x, y = FRAME_X + 220, FRAME_Y + 120
371- s.text(FRAME_X + FRAME_W / 2, y, "👋 Bienvenue dans Sidekicks", 26, TEXT, bold=True, anchor="middle")
372- s.text(FRAME_X + FRAME_W / 2, y + 34, "Discutez avec des agents d'IA dans des channels, comme avec des collègues.", 15, MUTED, anchor="middle")
373- steps = [("1", "Ajoutez un agent", "Un assistant qui tourne sur votre ordinateur, par exemple avec mm."),
374- ("2", "Ouvrez un channel", "#général est déjà prêt. Créez-en d'autres par sujet."),
375- ("3", "Appelez-le avec @", "« @alice bonjour » : Alice rejoint le channel et vous répond.")]
371+ s.text(FRAME_X + FRAME_W / 2, y, "👋 Welcome to Sidekicks", 26, TEXT, bold=True, anchor="middle")
372+ s.text(FRAME_X + FRAME_W / 2, y + 34, "Talk with AI agents in channels, as you would with colleagues.", 15, MUTED, anchor="middle")
373+ steps = [("1", "Add an agent", "An assistant that runs on your computer, for example with mm."),
374+ ("2", "Open a channel", "#general is ready. Create others, one per topic."),
375+ ("3", "Call it with @", "\"@alice hello\": Alice joins the channel and answers you.")]
376376 for index, (number, title, body) in enumerate(steps):
377377 top = y + 80 + index * 92
378378 s.rect(x, top, 800, 76, PANEL, BORDER, rx=12)
@@ -381,21 +381,21 @@ def screen_first_launch():
381381 s.text(x + 40, top + 44, number, 16, "#FFFFFF", bold=True, anchor="middle")
382382 s.text(x + 76, top + 32, title, 16, TEXT, bold=True)
383383 s.text(x + 76, top + 56, body, 13.5, MUTED)
384- s.button(x + 180, y + 370, 240, "+ Ajouter mon premier agent", primary=True, h=40)
385- s.button(x + 440, y + 370, 200, "Ouvrir une configuration…", h=40)
386- s.text(FRAME_X + FRAME_W / 2, y + 450, "Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)", 12, MUTED, anchor="middle")
384+ s.button(x + 180, y + 370, 240, "+ Add my first agent", primary=True, h=40)
385+ s.button(x + 440, y + 370, 200, "Open a configuration…", h=40)
386+ s.text(FRAME_X + FRAME_W / 2, y + 450, "Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)", 12, MUTED, anchor="middle")
387387 s.rect(x, y + 480, 800, 70, "#FDECEF", DANGER, rx=10)
388- s.text(x + 20, y + 508, "La commande « mm » est introuvable.", 14, DANGER, bold=True)
389- s.text(x + 20, y + 532, "Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent.", 13, TEXT)
388+ s.text(x + 20, y + 508, "The command \"mm\" cannot be found.", 14, DANGER, bold=True)
389+ s.text(x + 20, y + 532, "Install it (link to the guide) or choose another program for this agent.", 13, TEXT)
390390 s.badge(x + 812, y + 112, 1)
391391 s.badge(x + 652, y + 390, 2)
392392 s.badge(FRAME_X + FRAME_W / 2 + 390, y + 446, 3)
393393 s.badge(x + 812, y + 500, 4)
394394 s.notes([
395- (1, "Trois étapes, pas plus", ["L'état vide explique le modèle mental :", "agents, channels, @ (#33)."]),
396- (2, "Un assistant plutôt que du YAML", ["Le formulaire écrit le YAML à la place de", "l'utilisateur ; Settings reste accessible."]),
397- (3, "Rien à créer à la main", ["Le fichier par défaut existe déjà, avec des", "commentaires (#9)."]),
398- (4, "Erreurs humaines", ["Dire ce qui ne va pas et quoi faire ;", "jamais de trace technique (#33)."]),
395+ (1, "Three steps, no more", ["The empty state explains the mental model:", "agents, channels, @ (#33)."]),
396+ (2, "An assistant rather than YAML", ["The form writes the YAML for the user;", "Settings stays at hand."]),
397+ (3, "Nothing to create by hand", ["The default file already exists, with", "comments (#9)."]),
398+ (4, "Human errors", ["Say what is wrong and what to do;", "never a technical trace (#33)."]),
399399 ])
400400 return s
401401
@@ -98,7 +98,7 @@ class Screen:
98 98
99 # -- common pieces of the window -------------------------------------------------99 # -- common pieces of the window -------------------------------------------------
100 100
101- def window(self, status="● 2 agents actifs sur 2 · config : ~/agents/equipe.yaml"):101+ def window(self, status="● 2 of 2 agents running · config: ~/agents/team.yaml"):
102 """The window chrome: title bar, left dock, status bar."""102 """The window chrome: title bar, left dock, status bar."""
103 self.rect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, BG, "#C9CBD1", rx=12, shadow=True)103 self.rect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, BG, "#C9CBD1", rx=12, shadow=True)
104 self.rect(FRAME_X, FRAME_Y, FRAME_W, 36, PANEL2, None, rx=12)104 self.rect(FRAME_X, FRAME_Y, FRAME_W, 36, PANEL2, None, rx=12)
@@ -111,12 +111,12 @@ class Screen:
111 self.text(FRAME_X + 12, FRAME_Y + FRAME_H - 9, status, 11.5, MUTED)111 self.text(FRAME_X + 12, FRAME_Y + FRAME_H - 9, status, 11.5, MUTED)
112 self.text(FRAME_X + FRAME_W - 12, FRAME_Y + FRAME_H - 9, "⚙︎ Settings ⌘K", 11.5, MUTED, anchor="end")112 self.text(FRAME_X + FRAME_W - 12, FRAME_Y + FRAME_H - 9, "⚙︎ Settings ⌘K", 11.5, MUTED, anchor="end")
113 113
114- def left_dock(self, active="général", agents=(("alice", "👩 Alice", OK), ("bob", "🧔 Bob", OK), ("riker", "🧑‍🚀 Riker", MUTED), ("garfield", "🐮 Garfield", WARN))):114+ def left_dock(self, active="general", agents=(("alice", "👩 Alice", OK), ("bob", "🧔 Bob", OK), ("riker", "🧑‍🚀 Riker", MUTED), ("garfield", "🐮 Garfield", WARN))):
115 x, y = FRAME_X, FRAME_Y + 36115 x, y = FRAME_X, FRAME_Y + 36
116 self.rect(x, y, 230, FRAME_H - 62, PANEL, BORDER)116 self.rect(x, y, 230, FRAME_H - 62, PANEL, BORDER)
117 self.text(x + 16, y + 28, "CHANNELS", 11, MUTED, bold=True)117 self.text(x + 16, y + 28, "CHANNELS", 11, MUTED, bold=True)
118 self.text(x + 214, y + 28, "+", 16, MUTED, bold=True, anchor="end")118 self.text(x + 214, y + 28, "+", 16, MUTED, bold=True, anchor="end")
119- for index, name in enumerate(("général", "traductions", "code")):119+ for index, name in enumerate(("general", "translations", "code")):
120 top = y + 42 + index * 30120 top = y + 42 + index * 30
121 if name == active:121 if name == active:
122 self.rect(x + 8, top, 214, 26, PANEL2, None, rx=6)122 self.rect(x + 8, top, 214, 26, PANEL2, None, rx=6)
@@ -136,7 +136,7 @@ class Screen:
136 self.lines(x, y + 22, lines, 13.5)136 self.lines(x, y + 22, lines, 13.5)
137 return y + 22 + 20 * len(lines) + 14137 return y + 22 + 20 * len(lines) + 14
138 138
139- def composer(self, x, y, w, value, hint="Entrée pour envoyer · Maj+Entrée nouvelle ligne"):139+ def composer(self, x, y, w, value, hint="Enter to send · Shift+Enter for a new line"):
140 self.rect(x, y, w, 64, PANEL, ACCENT, rx=10, width=1.5)140 self.rect(x, y, w, 64, PANEL, ACCENT, rx=10, width=1.5)
141 self.text(x + 14, y + 26, value, 14)141 self.text(x + 14, y + 26, value, 14)
142 self.text(x + 14, y + 50, hint, 11, MUTED)142 self.text(x + 14, y + 50, hint, 11, MUTED)
@@ -163,98 +163,98 @@ CENTER_W = FRAME_W - 231
163 163
164 164
165 def screen_layout():165 def screen_layout():
166- s = Screen("01-layout", "1 · Layout global", "Docks façon Zed, mais seul l'essentiel est visible par défaut")166+ s = Screen("01-layout", "1 · Overall layout", "Zed-like docks, but only the essentials are visible by default")
167 s.window()167 s.window()
168 s.left_dock()168 s.left_dock()
169 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W - 300, 44, PANEL, BORDER)169 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W - 300, 44, PANEL, BORDER)
170- s.text(CENTER_X + 16, FRAME_Y + 64, "# général", 15, bold=True)170+ s.text(CENTER_X + 16, FRAME_Y + 64, "# general", 15, bold=True)
171 s.text(CENTER_X + CENTER_W - 316, FRAME_Y + 64, "👩 Alice · 🧔 Bob", 13, MUTED, anchor="end")171 s.text(CENTER_X + CENTER_W - 316, FRAME_Y + 64, "👩 Alice · 🧔 Bob", 13, MUTED, anchor="end")
172- y = s.message(CENTER_X + 24, FRAME_Y + 116, "Vous", TEXT, ["@alice résume-moi ce que fait ACP en deux phrases"])172+ y = s.message(CENTER_X + 24, FRAME_Y + 116, "You", TEXT, ["@alice sum up what ACP does in two sentences"])
173- y = s.message(CENTER_X + 24, y, "👩 Alice", AGENT_COLOURS["alice"], ["ACP est un protocole qui permet à une application de dialoguer avec", "n'importe quel agent d'IA. Sidekicks l'utilise pour rester agnostique."])173+ y = s.message(CENTER_X + 24, y, "👩 Alice", AGENT_COLOURS["alice"], ["ACP is a protocol that lets an application talk with any AI agent.", "Sidekicks uses it to stay agent-agnostic."])
174- s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 136, CENTER_W - 340, "Écrivez un message… (@ pour un agent, / pour une commande)")174+ s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 136, CENTER_W - 340, "Write a message… (@ for an agent, / for a command)")
175 # Right dock (Settings), shown here opened.175 # Right dock (Settings), shown here opened.
176 s.rect(FRAME_X + FRAME_W - 300, FRAME_Y + 36, 300, FRAME_H - 62, PANEL, BORDER)176 s.rect(FRAME_X + FRAME_W - 300, FRAME_Y + 36, 300, FRAME_H - 62, PANEL, BORDER)
177- s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 64, "⚙︎ Settings — equipe.yaml", 13, MUTED, bold=True)177+ s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 64, "⚙︎ Settings — team.yaml", 13, MUTED, bold=True)
178 s.rect(FRAME_X + FRAME_W - 290, FRAME_Y + 80, 280, 300, CODE, BORDER, rx=6)178 s.rect(FRAME_X + FRAME_W - 290, FRAME_Y + 80, 280, 300, CODE, BORDER, rx=6)
179- s.lines(FRAME_X + FRAME_W - 280, FRAME_Y + 104, ["version: 1", "agents:", " - name: alice", " title: Alice", " command: mm", " args: [-acp, ...]", "channels:", " - name: général", " agents: [alice, bob]"], 12, TEXT, gap=19, mono=True)179+ s.lines(FRAME_X + FRAME_W - 280, FRAME_Y + 104, ["version: 1", "agents:", " - name: alice", " title: Alice", " command: mm", " args: [-acp, ...]", "channels:", " - name: general", " agents: [alice, bob]"], 12, TEXT, gap=19, mono=True)
180- s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 410, "✓ Configuration valide", 12.5, OK, bold=True)180+ s.text(FRAME_X + FRAME_W - 284, FRAME_Y + 410, "✓ Valid configuration", 12.5, OK, bold=True)
181- s.button(FRAME_X + FRAME_W - 290, FRAME_Y + 428, 120, "Enregistrer", primary=True)181+ s.button(FRAME_X + FRAME_W - 290, FRAME_Y + 428, 120, "Save", primary=True)
182 # Collapsed bottom dock.182 # Collapsed bottom dock.
183 s.rect(CENTER_X, FRAME_Y + FRAME_H - 26 - 30, CENTER_W - 300, 30, PANEL2, BORDER)183 s.rect(CENTER_X, FRAME_Y + FRAME_H - 26 - 30, CENTER_W - 300, 30, PANEL2, BORDER)
184- s.text(CENTER_X + 14, FRAME_Y + FRAME_H - 36, "▸ Terminal · Fichiers (repliés)", 12, MUTED)184+ s.text(CENTER_X + 14, FRAME_Y + FRAME_H - 36, "▸ Terminal · Files (folded)", 12, MUTED)
185 s.badge(FRAME_X + 200, FRAME_Y + 62, 1)185 s.badge(FRAME_X + 200, FRAME_Y + 62, 1)
186 s.badge(CENTER_X + 120, FRAME_Y + 160, 2)186 s.badge(CENTER_X + 120, FRAME_Y + 160, 2)
187 s.badge(FRAME_X + FRAME_W - 30, FRAME_Y + 60, 3)187 s.badge(FRAME_X + FRAME_W - 30, FRAME_Y + 60, 3)
188 s.badge(CENTER_X + 330, FRAME_Y + FRAME_H - 41, 4)188 s.badge(CENTER_X + 330, FRAME_Y + FRAME_H - 41, 4)
189 s.badge(FRAME_X + 420, FRAME_Y + FRAME_H - 13, 5)189 s.badge(FRAME_X + 420, FRAME_Y + FRAME_H - 13, 5)
190 s.notes([190 s.notes([
191- (1, "Dock gauche : channels et agents", ["Toujours visible. La pastille donne l'état de", "l'agent : vert prêt, orange occupé, gris arrêté."]),191+ (1, "Left dock: channels and agents", ["Always visible. The dot gives the agent's", "state: green ready, orange busy, grey stopped."]),
192- (2, "Centre : la conversation", ["Le seul panel indispensable. Pleine largeur", "quand les docks sont repliés."]),192+ (2, "Centre: the conversation", ["The only panel you cannot do without.", "Full width when the docks are folded."]),
193- (3, "Dock droit : Settings", ["Éditeur YAML (CodeMirror) avec validation", "en direct. Fermé par défaut ; s'ouvre", "par ⚙︎ ou ⌘,."]),193+ (3, "Right dock: Settings", ["YAML editor (CodeMirror) with live", "validation. Closed by default; opens", "with ⚙︎ or ⌘,."]),
194- (4, "Dock bas : terminal et fichiers", ["Replié par défaut : utile aux curieux,", "jamais imposé au citizen user."]),194+ (4, "Bottom dock: terminal and files", ["Folded by default: there for the curious,", "never forced on the citizen user."]),
195- (5, "Barre d'état", ["Rappelle la limite d'agents simultanés", "(mémoire) et la configuration ouverte."]),195+ (5, "Status bar", ["Shows the limit of agents running at once", "(memory) and the open configuration."]),
196 ])196 ])
197 return s197 return s
198 198
199 199
200 def screen_conversation():200 def screen_conversation():
201- s = Screen("02-conversation", "2 · Conversation multi-agents", "Chaque agent a sa couleur ; les outils sont repliés ; on peut toujours arrêter")201+ s = Screen("02-conversation", "2 · Multi-agent conversation", "Each agent has its colour; tools are folded; you can always stop")
202 s.window()202 s.window()
203 s.left_dock()203 s.left_dock()
204 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)204 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)
205- s.text(CENTER_X + 16, FRAME_Y + 64, "# général", 15, bold=True)205+ s.text(CENTER_X + 16, FRAME_Y + 64, "# general", 15, bold=True)
206 s.text(CENTER_X + CENTER_W - 16, FRAME_Y + 64, "👩 Alice · 🧔 Bob · 🧑‍🚀 Riker", 13, MUTED, anchor="end")206 s.text(CENTER_X + CENTER_W - 16, FRAME_Y + 64, "👩 Alice · 🧔 Bob · 🧑‍🚀 Riker", 13, MUTED, anchor="end")
207 x = CENTER_X + 24207 x = CENTER_X + 24
208- y = s.message(x, FRAME_Y + 116, "", "", ["— Riker a rejoint le channel —"], system=True)208+ y = s.message(x, FRAME_Y + 116, "", "", ["— Riker joined the channel —"], system=True)
209- y = s.message(x, y + 6, "Vous", TEXT, ["@alice propose un nom pour un script qui sauvegarde mes photos"])209+ y = s.message(x, y + 6, "You", TEXT, ["@alice suggest a name for a script that backs up my photos"])
210- y = s.message(x, y, "👩 Alice", AGENT_COLOURS["alice"], ["Je propose « photo-backup.sh » : court, explicite, et l'extension dit que", "c'est un script shell."])210+ y = s.message(x, y, "👩 Alice", AGENT_COLOURS["alice"], ["I suggest \"photo-backup.sh\": short, clear, and the extension says", "it is a shell script."])
211- y = s.message(x, y, "Vous", TEXT, ["@bob tu valides ? @riker crée le fichier vide"])211+ y = s.message(x, y, "You", TEXT, ["@bob do you agree? @riker create the empty file"])
212- y = s.message(x, y, "🧔 Bob", AGENT_COLOURS["bob"], ["Bon choix. Alice a raison : un nom en minuscules, sans espace."])212+ y = s.message(x, y, "🧔 Bob", AGENT_COLOURS["bob"], ["Good choice. Alice is right: a lowercase name, no spaces."])
213 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)213 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)
214- s.text(x + 90, y, "travaille…", 12.5, MUTED, italic=True)214+ s.text(x + 90, y, "working…", 12.5, MUTED, italic=True)
215 s.rect(x, y + 12, 560, 40, PANEL2, BORDER, rx=8)215 s.rect(x, y + 12, 560, 40, PANEL2, BORDER, rx=8)
216- s.text(x + 14, y + 37, "▸ 🛠 write_file : photo-backup.sh (0 octet) ✓ terminé", 12.5, TEXT)216+ s.text(x + 14, y + 37, "▸ 🛠 write_file: photo-backup.sh (0 bytes) ✓ done", 12.5, TEXT)
217 s.rect(x, y + 60, 560, 40, PANEL2, BORDER, rx=8)217 s.rect(x, y + 60, 560, 40, PANEL2, BORDER, rx=8)
218- s.text(x + 14, y + 85, "▸ 🛠 bash : ls -la … en cours", 12.5, TEXT)218+ s.text(x + 14, y + 85, "▸ 🛠 bash: ls -la … running", 12.5, TEXT)
219 s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 106, CENTER_W - 160, "")219 s.composer(CENTER_X + 20, FRAME_Y + FRAME_H - 106, CENTER_W - 160, "")
220- s.button(CENTER_X + CENTER_W - 128, FRAME_Y + FRAME_H - 90, 108, "■ Arrêter", danger=True)220+ s.button(CENTER_X + CENTER_W - 128, FRAME_Y + FRAME_H - 90, 108, "■ Stop", danger=True)
221 s.rect(CENTER_X + CENTER_W - 220, FRAME_Y + 96, 200, 30, PANEL2, BORDER, rx=15)221 s.rect(CENTER_X + CENTER_W - 220, FRAME_Y + 96, 200, 30, PANEL2, BORDER, rx=15)
222- s.text(CENTER_X + CENTER_W - 120, FRAME_Y + 116, "contexte Riker : 41 %", 11.5, MUTED, anchor="middle")222+ s.text(CENTER_X + CENTER_W - 120, FRAME_Y + 116, "Riker's context: 41%", 11.5, MUTED, anchor="middle")
223 s.badge(x + 300, FRAME_Y + 111, 1)223 s.badge(x + 300, FRAME_Y + 111, 1)
224 s.badge(x + 80, FRAME_Y + 194, 2)224 s.badge(x + 80, FRAME_Y + 194, 2)
225 s.badge(x + 580, y + 32, 3)225 s.badge(x + 580, y + 32, 3)
226 s.badge(CENTER_X + CENTER_W - 140, FRAME_Y + FRAME_H - 90, 4)226 s.badge(CENTER_X + CENTER_W - 140, FRAME_Y + FRAME_H - 90, 4)
227 s.badge(CENTER_X + CENTER_W - 232, FRAME_Y + 111, 5)227 s.badge(CENTER_X + CENTER_W - 232, FRAME_Y + 111, 5)
228 s.notes([228 s.notes([
229- (1, "Arrivée visible", ["La 1re mention invite l'agent : un message", "système le dit. On sait qui « écoute »."]),229+ (1, "A visible arrival", ["The first mention invites the agent: a system", "message says so. You know who is listening."]),
230- (2, "Une couleur par agent", ["Avatar + nom colorés, réponses en Markdown", "avec code coloré (ADR 0002)."]),230+ (2, "One colour per agent", ["Coloured avatar and name, answers in", "Markdown with highlighted code (ADR 0002)."]),
231- (3, "Outils repliés", ["Une ligne par tool call : titre lisible et", "statut. Déplier montre la commande, le", "diff ou la sortie."]),231+ (3, "Folded tools", ["One line per tool call: a readable title", "and its status. Unfolding shows the", "command, the diff or the output."]),
232- (4, "Toujours un bouton Arrêter", ["Envoie session/cancel aux agents qui", "répondent dans ce channel (ADR 0001)."]),232+ (4, "Always a Stop button", ["Sends session/cancel to the agents", "answering in this channel (ADR 0001)."]),
233- (5, "Jauge de contexte", ["usage_update : prévient avant que la", "conversation ne dépasse la mémoire de", "l'agent."]),233+ (5, "Context gauge", ["usage_update: warns before the", "conversation outgrows the agent's", "memory."]),
234 ])234 ])
235 return s235 return s
236 236
237 237
238 def screen_popups():238 def screen_popups():
239- s = Screen("03-mentions-commands", "3 · Invoquer un agent (@) et une commande (/)", "Tab choisit la suggestion ; continuer à écrire filtre la liste")239+ s = Screen("03-mentions-commands", "3 · Calling an agent (@) and a command (/)", "Tab picks the suggestion; typing on filters the list")
240 s.window()240 s.window()
241 s.left_dock()241 s.left_dock()
242 x, w = CENTER_X + 20, CENTER_W - 40242 x, w = CENTER_X + 20, CENTER_W - 40
243 # @ popup (top half).243 # @ popup (top half).
244- s.text(x, FRAME_Y + 70, "Mention d'un agent", 14, MUTED, bold=True)244+ s.text(x, FRAME_Y + 70, "Mentioning an agent", 14, MUTED, bold=True)
245 s.rect(x, FRAME_Y + 84, 420, 150, PANEL, BORDER, rx=10, shadow=True)245 s.rect(x, FRAME_Y + 84, 420, 150, PANEL, BORDER, rx=10, shadow=True)
246- rows = [("🐮 garfield", "Garfield (traducteur) — traduit FR ⇄ EN", True), ("🧑‍🚀 riker", "Riker (outils) — commandes et fichiers", False), ("👩 alice", "Alice (assistante) — questions simples", False)]246+ rows = [("🐮 garfield", "Garfield (translator) — translates FR ⇄ EN", True), ("🧑‍🚀 riker", "Riker (tools) — commands and files", False), ("👩 alice", "Alice (assistant) — simple questions", False)]
247 for index, (name, description, selected) in enumerate(rows):247 for index, (name, description, selected) in enumerate(rows):
248 top = FRAME_Y + 92 + index * 44248 top = FRAME_Y + 92 + index * 44
249 if selected:249 if selected:
250 s.rect(x + 6, top, 408, 40, "#E8EFFD", None, rx=6)250 s.rect(x + 6, top, 408, 40, "#E8EFFD", None, rx=6)
251 s.text(x + 18, top + 18, name, 13.5, TEXT, bold=True)251 s.text(x + 18, top + 18, name, 13.5, TEXT, bold=True)
252 s.text(x + 18, top + 34, description, 11.5, MUTED)252 s.text(x + 18, top + 34, description, 11.5, MUTED)
253- s.composer(x, FRAME_Y + 244, w, "@ga", "Tab : @garfield · ↑↓ naviguer · Échap fermer")253+ s.composer(x, FRAME_Y + 244, w, "@ga", "Tab: @garfield · ↑↓ move · Esc close")
254 # / popup (bottom half).254 # / popup (bottom half).
255- s.text(x, FRAME_Y + 360, "Commande d'un agent (plusieurs agents dans le channel)", 14, MUTED, bold=True)255+ s.text(x, FRAME_Y + 360, "An agent's command (several agents in the channel)", 14, MUTED, bold=True)
256 s.rect(x, FRAME_Y + 374, 520, 196, PANEL, BORDER, rx=10, shadow=True)256 s.rect(x, FRAME_Y + 374, 520, 196, PANEL, BORDER, rx=10, shadow=True)
257- commands = [("/garfield:idioms", "Expressions idiomatiques et faux amis", "[texte]", True), ("/garfield:styles", "Plusieurs versions d'une traduction", "[texte]", False), ("/garfield:email-letter", "E-mails et lettres : formules, tu ou vous", "[texte]", False), ("/alice:compact", "Résumer l'historique maintenant", "", False)]257+ commands = [("/garfield:idioms", "Idioms and false friends", "[text]", True), ("/garfield:styles", "Several versions of a translation", "[text]", False), ("/garfield:email-letter", "E-mails and letters: greetings, tu or vous", "[text]", False), ("/alice:compact", "Summarise the history now", "", False)]
258 for index, (name, description, hint, selected) in enumerate(commands):258 for index, (name, description, hint, selected) in enumerate(commands):
259 top = FRAME_Y + 382 + index * 46259 top = FRAME_Y + 382 + index * 46
260 if selected:260 if selected:
@@ -262,90 +262,90 @@ def screen_popups():
262 s.text(x + 18, top + 19, name, 13.5, TEXT, bold=True, mono=True)262 s.text(x + 18, top + 19, name, 13.5, TEXT, bold=True, mono=True)
263 s.text(x + 500, top + 19, hint, 11.5, MUTED, anchor="end", mono=True)263 s.text(x + 500, top + 19, hint, 11.5, MUTED, anchor="end", mono=True)
264 s.text(x + 18, top + 36, description, 11.5, MUTED)264 s.text(x + 18, top + 36, description, 11.5, MUTED)
265- s.composer(x, FRAME_Y + 580, w, "/gar", "Tab : /garfield:idioms · le texte après la commande est son argument")265+ s.composer(x, FRAME_Y + 580, w, "/gar", "Tab: /garfield:idioms · the text after the command is its argument")
266 s.badge(x + 434, FRAME_Y + 100, 1)266 s.badge(x + 434, FRAME_Y + 100, 1)
267 s.badge(x + 300, FRAME_Y + 276, 2)267 s.badge(x + 300, FRAME_Y + 276, 2)
268 s.badge(x + 534, FRAME_Y + 390, 3)268 s.badge(x + 534, FRAME_Y + 390, 3)
269 s.badge(x + 534, FRAME_Y + 530, 4)269 s.badge(x + 534, FRAME_Y + 530, 4)
270 s.badge(x + w - 20, FRAME_Y + 612, 5)270 s.badge(x + w - 20, FRAME_Y + 612, 5)
271 s.notes([271 s.notes([
272- (1, "@ liste les agents de la config", ["Nom technique + titre + description.", "Filtrage flou sur nom et titre (« gar »)."]),272+ (1, "@ lists the configured agents", ["Technical name + title + description.", "Fuzzy filter on name and title (\"gar\")."]),
273- (2, "Tab sélectionne, sinon on écrit", ["Comportement demandé dans le draft (#1).", "Échap ferme sans rien insérer."]),273+ (2, "Tab picks, otherwise you type", ["Behaviour asked for in the draft (#1).", "Esc closes without inserting anything."]),
274- (3, "/agent:commande en multi-agents", ["Avec un seul agent : /commande. Sidekicks", "envoie « /idioms … » au seul Garfield."]),274+ (3, "/agent:command with several agents", ["With one agent: /command. Sidekicks", "sends \"/idioms …\" to Garfield only."]),
275- (4, "Skills et commandes intégrées", ["mm annonce compact, new, unload et ses", "skills (available_commands_update)."]),275+ (4, "Skills and built-in commands", ["mm announces compact, new, unload and", "its skills (available_commands_update)."]),
276- (5, "Indice d'argument", ["input.hint de la commande ACP, affiché", "tant que l'argument n'est pas saisi."]),276+ (5, "Argument hint", ["The ACP command's input.hint, shown", "until the argument is typed."]),
277 ])277 ])
278 return s278 return s
279 279
280 280
281 def screen_permission():281 def screen_permission():
282- s = Screen("04-permission", "4 · Demande de permission", "En langage clair, sur la conversation concernée, avec une sortie de secours")282+ s = Screen("04-permission", "4 · Permission request", "In plain words, on the conversation concerned, with a way out")
283 s.window()283 s.window()
284 s.left_dock(active="code")284 s.left_dock(active="code")
285 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)285 s.rect(CENTER_X, FRAME_Y + 36, CENTER_W, 44, PANEL, BORDER)
286 s.text(CENTER_X + 16, FRAME_Y + 64, "# code", 15, bold=True)286 s.text(CENTER_X + 16, FRAME_Y + 64, "# code", 15, bold=True)
287 x = CENTER_X + 24287 x = CENTER_X + 24
288- y = s.message(x, FRAME_Y + 116, "Vous", TEXT, ["@riker supprime les fichiers temporaires du dossier"])288+ y = s.message(x, FRAME_Y + 116, "You", TEXT, ["@riker delete the temporary files in the folder"])
289 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)289 s.text(x, y, "🧑‍🚀 Riker", 13.5, AGENT_COLOURS["riker"], bold=True)
290- s.text(x + 90, y, "attend votre réponse", 12.5, WARN, italic=True)290+ s.text(x + 90, y, "waiting for your answer", 12.5, WARN, italic=True)
291 # The dialog, attached to the conversation (not a global modal).291 # The dialog, attached to the conversation (not a global modal).
292 dx, dy, dw = x, y + 18, 720292 dx, dy, dw = x, y + 18, 720
293 s.rect(dx, dy, dw, 330, PANEL, WARN, rx=12, width=2, shadow=True)293 s.rect(dx, dy, dw, 330, PANEL, WARN, rx=12, width=2, shadow=True)
294- s.text(dx + 20, dy + 34, "🧑‍🚀 Riker veut exécuter une commande sur votre ordinateur", 16, TEXT, bold=True)294+ s.text(dx + 20, dy + 34, "🧑‍🚀 Riker wants to run a command on your computer", 16, TEXT, bold=True)
295 s.rect(dx + 20, dy + 52, dw - 40, 40, CODE, BORDER, rx=6)295 s.rect(dx + 20, dy + 52, dw - 40, 40, CODE, BORDER, rx=6)
296 s.text(dx + 34, dy + 77, "rm -f *.tmp", 14, TEXT, mono=True)296 s.text(dx + 34, dy + 77, "rm -f *.tmp", 14, TEXT, mono=True)
297- s.lines(dx + 20, dy + 118, ["Ce que cela fait : supprime définitivement les fichiers se terminant par .tmp", "dans agents/riker . Ils ne passent pas par la corbeille."], 13, TEXT, gap=20)297+ s.lines(dx + 20, dy + 118, ["What it does: permanently deletes the files ending in .tmp", "in agents/riker . They do not go to the trash."], 13, TEXT, gap=20)
298- s.text(dx + 20, dy + 168, "⚠ Action irréversible", 13, DANGER, bold=True)298+ s.text(dx + 20, dy + 168, "⚠ Cannot be undone", 13, DANGER, bold=True)
299- s.button(dx + 20, dy + 190, 170, "Autoriser une fois", primary=True)299+ s.button(dx + 20, dy + 190, 170, "Allow once", primary=True)
300- s.button(dx + 200, dy + 190, 300, "Toujours autoriser bash ici")300+ s.button(dx + 200, dy + 190, 300, "Always allow bash here")
301- s.button(dx + 20, dy + 234, 170, "Refuser")301+ s.button(dx + 20, dy + 234, 170, "Reject")
302- s.button(dx + 200, dy + 234, 300, "Ne plus jamais proposer bash ici")302+ s.button(dx + 200, dy + 234, 300, "Never offer bash here again")
303- s.button(dx + 520, dy + 234, 180, "■ Arrêter Riker", danger=True)303+ s.button(dx + 520, dy + 234, 180, "■ Stop Riker", danger=True)
304- s.text(dx + 20, dy + 300, "▸ Détails techniques (tool call bash, rawInput)", 12, MUTED)304+ s.text(dx + 20, dy + 300, "▸ Technical details (bash tool call, rawInput)", 12, MUTED)
305 s.badge(dx + dw - 16, dy + 30, 1)305 s.badge(dx + dw - 16, dy + 30, 1)
306 s.badge(dx + 680, dy + 128, 2)306 s.badge(dx + 680, dy + 128, 2)
307 s.badge(dx + 512, dy + 206, 3)307 s.badge(dx + 512, dy + 206, 3)
308 s.badge(dx + 712, dy + 250, 4)308 s.badge(dx + 712, dy + 250, 4)
309 s.badge(dx + 380, dy + 296, 5)309 s.badge(dx + 380, dy + 296, 5)
310 s.notes([310 s.notes([
311- (1, "Attaché à la conversation", ["Bloque seulement la session de Riker ; les", "autres channels continuent (#17)."]),311+ (1, "Attached to the conversation", ["Blocks only Riker's session; the other", "channels go on (#17)."]),
312- (2, "Expliquer, pas montrer du JSON", ["Titre ACP + kind + rawInput traduits en", "phrase. Les actions destructives sont", "signalées."]),312+ (2, "Explain, do not show JSON", ["ACP title + kind + rawInput turned into", "a sentence. Destructive actions are", "flagged."]),
313- (3, "Les 4 options ACP, par ids", ["allow · allow_always · reject · never.", "« Ne plus jamais » est mis en avant au", "lieu de « Refuser une fois »."]),313+ (3, "The 4 ACP options, by id", ["allow · allow_always · reject · never.", "\"Never again\" is put forward rather", "than \"Reject once\"."]),
314- (4, "Toujours une sortie de secours", ["Spike ACP : après un refus, un petit modèle", "a réessayé 40 fois. Arrêter = session/cancel."]),314+ (4, "Always a way out", ["ACP spike: after a rejection, a small model", "retried 40 times. Stop = session/cancel."]),
315- (5, "Détails pour les curieux", ["Repliés par défaut."]),315+ (5, "Details for the curious", ["Folded by default."]),
316 ])316 ])
317 return s317 return s
318 318
319 319
320 def screen_agents():320 def screen_agents():
321- s = Screen("05-agents-panel", "5 · Panel Agents : fichiers, éditeur, terminal", "Le workspace de l'agent, qu'il tourne en local, dans un conteneur ou une sandbox sbx")321+ s = Screen("05-agents-panel", "5 · Agents panel: files, editor, terminal", "The agent's workspace, whether it runs locally, in a container or in an sbx sandbox")
322 s.window()322 s.window()
323 s.left_dock(active="")323 s.left_dock(active="")
324 x, y = CENTER_X, FRAME_Y + 36324 x, y = CENTER_X, FRAME_Y + 36
325 s.rect(x, y, CENTER_W, 56, PANEL, BORDER)325 s.rect(x, y, CENTER_W, 56, PANEL, BORDER)
326- s.text(x + 16, y + 26, "🐮 Garfield (traducteur)", 16, bold=True)326+ s.text(x + 16, y + 26, "🐮 Garfield (translator)", 16, bold=True)
327- s.text(x + 16, y + 46, "● occupé · local · agents/garfield · gemma-4-E4B", 12, WARN)327+ s.text(x + 16, y + 46, "● busy · local · agents/garfield · gemma-4-E4B", 12, WARN)
328- s.button(x + CENTER_W - 250, y + 12, 110, "Redémarrer")328+ s.button(x + CENTER_W - 250, y + 12, 110, "Restart")
329- s.button(x + CENTER_W - 130, y + 12, 110, "Arrêter", danger=True)329+ s.button(x + CENTER_W - 130, y + 12, 110, "Stop", danger=True)
330- tabs = ["Fichiers", "Terminal", "Journal"]330+ tabs = ["Files", "Terminal", "Log"]
331 for index, tab in enumerate(tabs):331 for index, tab in enumerate(tabs):
332 s.text(x + 24 + index * 100, y + 82, tab, 13.5, ACCENT if index == 0 else MUTED, bold=index == 0)332 s.text(x + 24 + index * 100, y + 82, tab, 13.5, ACCENT if index == 0 else MUTED, bold=index == 0)
333 s.rect(x + 16, y + 90, 70, 2, ACCENT, None)333 s.rect(x + 16, y + 90, 70, 2, ACCENT, None)
334 # Tree.334 # Tree.
335 s.rect(x, y + 100, 260, 420, PANEL, BORDER)335 s.rect(x, y + 100, 260, 420, PANEL, BORDER)
336- tree = ["▾ garfield", " AGENTS.md", " README.md", " agent.yaml", " ▾ skills", " ▸ email-letter", " ▸ idioms", " ▸ styles", " ▸ technical-text", " traduction.md"]336+ tree = ["▾ garfield", " AGENTS.md", " README.md", " agent.yaml", " ▾ skills", " ▸ email-letter", " ▸ idioms", " ▸ styles", " ▸ technical-text", " translation.md"]
337 for index, item in enumerate(tree):337 for index, item in enumerate(tree):
338 if item.strip() == "AGENTS.md":338 if item.strip() == "AGENTS.md":
339 s.rect(x + 6, y + 112 + index * 26, 248, 24, "#E8EFFD", None, rx=4)339 s.rect(x + 6, y + 112 + index * 26, 248, 24, "#E8EFFD", None, rx=4)
340 s.text(x + 14, y + 129 + index * 26, item, 12.5, TEXT, mono=True)340 s.text(x + 14, y + 129 + index * 26, item, 12.5, TEXT, mono=True)
341- s.text(x + 14, y + 400, "+ Nouveau fichier", 12.5, ACCENT)341+ s.text(x + 14, y + 400, "+ New file", 12.5, ACCENT)
342 # Editor.342 # Editor.
343 ex = x + 270343 ex = x + 270
344 s.rect(ex, y + 100, CENTER_W - 280, 420, CODE, BORDER)344 s.rect(ex, y + 100, CENTER_W - 280, 420, CODE, BORDER)
345 s.text(ex + 14, y + 124, "AGENTS.md", 13, MUTED, bold=True)345 s.text(ex + 14, y + 124, "AGENTS.md", 13, MUTED, bold=True)
346- s.lines(ex + 14, y + 154, ["# Style de traduction", "", "- Registre : celui du texte source.", "- Typographie française : espaces insécables", " avant ; : ! ? et entre « guillemets ».", "- Ne jamais traduire le code ni les noms propres."], 12.5, TEXT, gap=22, mono=True)346+ s.lines(ex + 14, y + 154, ["# Translation style", "", "- Register: the one of the source text.", "- French typography: non-breaking spaces", " before ; : ! ? and inside « quotes ».", "- Never translate code or proper names."], 12.5, TEXT, gap=22, mono=True)
347- s.button(ex + CENTER_W - 280 - 240, y + 474, 110, "Enregistrer", primary=True)347+ s.button(ex + CENTER_W - 280 - 240, y + 474, 110, "Save", primary=True)
348- s.button(ex + CENTER_W - 280 - 120, y + 474, 110, "Supprimer", danger=True)348+ s.button(ex + CENTER_W - 280 - 120, y + 474, 110, "Delete", danger=True)
349 # Terminal (bottom).349 # Terminal (bottom).
350 s.rect(x, y + 530, CENTER_W, FRAME_H - 26 - 36 - 530, "#111317", BORDER)350 s.rect(x, y + 530, CENTER_W, FRAME_H - 26 - 36 - 530, "#111317", BORDER)
351 s.lines(x + 14, y + 556, ["garfield $ ls skills", "email-letter idioms styles technical-text", "garfield $ █"], 12.5, "#D7DAE0", gap=20, mono=True)351 s.lines(x + 14, y + 556, ["garfield $ ls skills", "email-letter idioms styles technical-text", "garfield $ █"], 12.5, "#D7DAE0", gap=20, mono=True)
@@ -355,24 +355,24 @@ def screen_agents():
355 s.badge(x + CENTER_W - 20, y + 552, 4)355 s.badge(x + CENTER_W - 20, y + 552, 4)
356 s.badge(x + 330, y + 82, 5)356 s.badge(x + 330, y + 82, 5)
357 s.notes([357 s.notes([
358- (1, "État et emplacement de l'agent", ["Statut temps réel (#11), runtime (local,", "container, sbx), dossier et modèle."]),358+ (1, "The agent's state and location", ["Live status (#11), runtime (local,", "container, sbx), folder and model."]),
359- (2, "Arbre du workspace", ["Chargement paresseux, rafraîchi quand", "l'agent modifie un fichier (#29)."]),359+ (2, "Workspace tree", ["Loaded lazily, refreshed when the", "agent changes a file (#29)."]),
360- (3, "Éditer, supprimer avec confirmation", ["Même éditeur que Settings. Conflit signalé", "si l'agent modifie le fichier (#30)."]),360+ (3, "Edit, delete with confirmation", ["Same editor as Settings. A conflict is", "flagged if the agent changes the file (#30)."]),
361- (4, "Terminal sur le workspace", ["PTY local, docker exec ou sbx selon le", "runtime (#28, #31). Replié par défaut."]),361+ (4, "Terminal in the workspace", ["Local PTY, docker exec or sbx depending on", "the runtime (#28, #31). Folded by default."]),
362- (5, "Journal", ["stderr de l'agent, pour comprendre", "une erreur sans ouvrir un terminal."]),362+ (5, "Log", ["The agent's stderr, to understand", "an error without opening a terminal."]),
363 ])363 ])
364 return s364 return s
365 365
366 366
367 def screen_first_launch():367 def screen_first_launch():
368- s = Screen("06-first-launch", "6 · Premier lancement", "Moins d'une minute pour savoir quoi faire, sans lire de documentation")368+ s = Screen("06-first-launch", "6 · First launch", "Less than a minute to know what to do, without reading any documentation")
369- s.window(status="○ aucun agent configuré · config : créée à l'emplacement par défaut")369+ s.window(status="○ no agent configured · config: created at the default location")
370 x, y = FRAME_X + 220, FRAME_Y + 120370 x, y = FRAME_X + 220, FRAME_Y + 120
371- s.text(FRAME_X + FRAME_W / 2, y, "👋 Bienvenue dans Sidekicks", 26, TEXT, bold=True, anchor="middle")371+ s.text(FRAME_X + FRAME_W / 2, y, "👋 Welcome to Sidekicks", 26, TEXT, bold=True, anchor="middle")
372- s.text(FRAME_X + FRAME_W / 2, y + 34, "Discutez avec des agents d'IA dans des channels, comme avec des collègues.", 15, MUTED, anchor="middle")372+ s.text(FRAME_X + FRAME_W / 2, y + 34, "Talk with AI agents in channels, as you would with colleagues.", 15, MUTED, anchor="middle")
373- steps = [("1", "Ajoutez un agent", "Un assistant qui tourne sur votre ordinateur, par exemple avec mm."),373+ steps = [("1", "Add an agent", "An assistant that runs on your computer, for example with mm."),
374- ("2", "Ouvrez un channel", "#général est déjà prêt. Créez-en d'autres par sujet."),374+ ("2", "Open a channel", "#general is ready. Create others, one per topic."),
375- ("3", "Appelez-le avec @", "« @alice bonjour » : Alice rejoint le channel et vous répond.")]375+ ("3", "Call it with @", "\"@alice hello\": Alice joins the channel and answers you.")]
376 for index, (number, title, body) in enumerate(steps):376 for index, (number, title, body) in enumerate(steps):
377 top = y + 80 + index * 92377 top = y + 80 + index * 92
378 s.rect(x, top, 800, 76, PANEL, BORDER, rx=12)378 s.rect(x, top, 800, 76, PANEL, BORDER, rx=12)
@@ -381,21 +381,21 @@ def screen_first_launch():
381 s.text(x + 40, top + 44, number, 16, "#FFFFFF", bold=True, anchor="middle")381 s.text(x + 40, top + 44, number, 16, "#FFFFFF", bold=True, anchor="middle")
382 s.text(x + 76, top + 32, title, 16, TEXT, bold=True)382 s.text(x + 76, top + 32, title, 16, TEXT, bold=True)
383 s.text(x + 76, top + 56, body, 13.5, MUTED)383 s.text(x + 76, top + 56, body, 13.5, MUTED)
384- s.button(x + 180, y + 370, 240, "+ Ajouter mon premier agent", primary=True, h=40)384+ s.button(x + 180, y + 370, 240, "+ Add my first agent", primary=True, h=40)
385- s.button(x + 440, y + 370, 200, "Ouvrir une configuration…", h=40)385+ s.button(x + 440, y + 370, 200, "Open a configuration…", h=40)
386- s.text(FRAME_X + FRAME_W / 2, y + 450, "Configuration : ~/Library/Application Support/sidekicks/config.yaml (créée pour vous, commentée)", 12, MUTED, anchor="middle")386+ s.text(FRAME_X + FRAME_W / 2, y + 450, "Configuration: ~/Library/Application Support/sidekicks/config.yaml (created for you, with comments)", 12, MUTED, anchor="middle")
387 s.rect(x, y + 480, 800, 70, "#FDECEF", DANGER, rx=10)387 s.rect(x, y + 480, 800, 70, "#FDECEF", DANGER, rx=10)
388- s.text(x + 20, y + 508, "La commande « mm » est introuvable.", 14, DANGER, bold=True)388+ s.text(x + 20, y + 508, "The command \"mm\" cannot be found.", 14, DANGER, bold=True)
389- s.text(x + 20, y + 532, "Installez-la (lien vers le guide) ou choisissez un autre programme pour cet agent.", 13, TEXT)389+ s.text(x + 20, y + 532, "Install it (link to the guide) or choose another program for this agent.", 13, TEXT)
390 s.badge(x + 812, y + 112, 1)390 s.badge(x + 812, y + 112, 1)
391 s.badge(x + 652, y + 390, 2)391 s.badge(x + 652, y + 390, 2)
392 s.badge(FRAME_X + FRAME_W / 2 + 390, y + 446, 3)392 s.badge(FRAME_X + FRAME_W / 2 + 390, y + 446, 3)
393 s.badge(x + 812, y + 500, 4)393 s.badge(x + 812, y + 500, 4)
394 s.notes([394 s.notes([
395- (1, "Trois étapes, pas plus", ["L'état vide explique le modèle mental :", "agents, channels, @ (#33)."]),395+ (1, "Three steps, no more", ["The empty state explains the mental model:", "agents, channels, @ (#33)."]),
396- (2, "Un assistant plutôt que du YAML", ["Le formulaire écrit le YAML à la place de", "l'utilisateur ; Settings reste accessible."]),396+ (2, "An assistant rather than YAML", ["The form writes the YAML for the user;", "Settings stays at hand."]),
397- (3, "Rien à créer à la main", ["Le fichier par défaut existe déjà, avec des", "commentaires (#9)."]),397+ (3, "Nothing to create by hand", ["The default file already exists, with", "comments (#9)."]),
398- (4, "Erreurs humaines", ["Dire ce qui ne va pas et quoi faire ;", "jamais de trace technique (#33)."]),398+ (4, "Human errors", ["Say what is wrong and what to do;", "never a technical trace (#33)."]),
399 ])399 ])
400 return s400 return s
401 401
modified docs/diagrams/packages.drawio +36 -34
@@ -1,50 +1,52 @@
11 <mxfile host="app.diagrams.net">
22 <diagram id="packages" name="Sidekicks — packages and dependencies">
3- <mxGraphModel dx="1410" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1410" pageHeight="800" background="#F5F7FC" math="0" shadow="0">
3+ <mxGraphModel dx="1690" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1690" pageHeight="800" background="#F5F7FC" math="0" shadow="0">
44 <root>
55 <mxCell id="0"/>
66 <mxCell id="1" parent="0"/>
7- <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;Sidekicks — packages and dependencies&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;arrows are imports · all packages exist (M4) · only main.go imports Wails&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="255.0" y="12" width="900" height="56" as="geometry"/></mxCell>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;Sidekicks — packages and dependencies&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;arrows are imports · all packages exist (v0.1) · only main.go imports Wails&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="395.0" y="12" width="900" height="56" as="geometry"/></mxCell>
88 <mxCell id="c5" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=block;startFill=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="157" as="sourcePoint"/><mxPoint x="590" y="157" as="targetPoint"/></mxGeometry></mxCell>
99 <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1150" y="157" as="sourcePoint"/><mxPoint x="820" y="157" as="targetPoint"/></mxGeometry></mxCell>
10- <mxCell id="c14" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="145" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="145" y="255"/></Array></mxGeometry></mxCell>
11- <mxCell id="c15" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="425" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="425" y="255"/></Array></mxGeometry></mxCell>
12- <mxCell id="c16" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="705" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="705" y="255"/></Array></mxGeometry></mxCell>
13- <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="985" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="985" y="255"/></Array></mxGeometry></mxCell>
14- <mxCell id="c18" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="1265" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="1265" y="255"/></Array></mxGeometry></mxCell>
15- <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="355" as="sourcePoint"/><mxPoint x="310" y="355" as="targetPoint"/></mxGeometry></mxCell>
16- <mxCell id="c20" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="540" y="355" as="sourcePoint"/><mxPoint x="590" y="355" as="targetPoint"/></mxGeometry></mxCell>
17- <mxCell id="c25" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="410" as="sourcePoint"/><mxPoint x="145" y="490" as="targetPoint"/></mxGeometry></mxCell>
18- <mxCell id="c26" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="410" as="sourcePoint"/><mxPoint x="425" y="490" as="targetPoint"/></mxGeometry></mxCell>
19- <mxCell id="c27" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="410" as="sourcePoint"/><mxPoint x="705" y="490" as="targetPoint"/></mxGeometry></mxCell>
20- <mxCell id="c28" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="520" y="410" as="sourcePoint"/><mxPoint x="985" y="490" as="targetPoint"/><Array as="points"><mxPoint x="520" y="450"/><mxPoint x="985" y="450"/></Array></mxGeometry></mxCell>
21- <mxCell id="c35" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1265" y="410" as="sourcePoint"/><mxPoint x="1265" y="490" as="targetPoint"/></mxGeometry></mxCell>
22- <mxCell id="c36" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="595" as="sourcePoint"/><mxPoint x="145" y="670" as="targetPoint"/></mxGeometry></mxCell>
23- <mxCell id="c37" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="595" as="sourcePoint"/><mxPoint x="425" y="670" as="targetPoint"/></mxGeometry></mxCell>
24- <mxCell id="c39" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="595" as="sourcePoint"/><mxPoint x="705" y="670" as="targetPoint"/></mxGeometry></mxCell>
10+ <mxCell id="c10" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1380" y="157" as="sourcePoint"/><mxPoint x="1430" y="157" as="targetPoint"/></mxGeometry></mxCell>
11+ <mxCell id="c16" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="145" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="145" y="255"/></Array></mxGeometry></mxCell>
12+ <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="425" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="425" y="255"/></Array></mxGeometry></mxCell>
13+ <mxCell id="c18" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="705" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="705" y="255"/></Array></mxGeometry></mxCell>
14+ <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="985" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="985" y="255"/></Array></mxGeometry></mxCell>
15+ <mxCell id="c20" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="1265" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="1265" y="255"/></Array></mxGeometry></mxCell>
16+ <mxCell id="c21" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="355" as="sourcePoint"/><mxPoint x="310" y="355" as="targetPoint"/></mxGeometry></mxCell>
17+ <mxCell id="c22" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="540" y="355" as="sourcePoint"/><mxPoint x="590" y="355" as="targetPoint"/></mxGeometry></mxCell>
18+ <mxCell id="c27" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="410" as="sourcePoint"/><mxPoint x="145" y="490" as="targetPoint"/></mxGeometry></mxCell>
19+ <mxCell id="c28" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="410" as="sourcePoint"/><mxPoint x="425" y="490" as="targetPoint"/></mxGeometry></mxCell>
20+ <mxCell id="c29" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="410" as="sourcePoint"/><mxPoint x="705" y="490" as="targetPoint"/></mxGeometry></mxCell>
21+ <mxCell id="c30" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="520" y="410" as="sourcePoint"/><mxPoint x="985" y="490" as="targetPoint"/><Array as="points"><mxPoint x="520" y="450"/><mxPoint x="985" y="450"/></Array></mxGeometry></mxCell>
22+ <mxCell id="c37" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1265" y="410" as="sourcePoint"/><mxPoint x="1265" y="490" as="targetPoint"/></mxGeometry></mxCell>
23+ <mxCell id="c38" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="595" as="sourcePoint"/><mxPoint x="145" y="670" as="targetPoint"/></mxGeometry></mxCell>
24+ <mxCell id="c39" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="595" as="sourcePoint"/><mxPoint x="425" y="670" as="targetPoint"/></mxGeometry></mxCell>
25+ <mxCell id="c41" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="595" as="sourcePoint"/><mxPoint x="705" y="670" as="targetPoint"/></mxGeometry></mxCell>
2526 <mxCell id="c1" value="💻&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;frontend/&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Svelte 5 · panels · chat · @ /&lt;br&gt;files · xterm.js · bindings&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="30" y="100" width="230" height="115" as="geometry"/></mxCell>
2627 <mxCell id="c2" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/app&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Agent · Channel · Config&lt;br&gt;Workspace · Terminal&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="590" y="100" width="230" height="115" as="geometry"/></mxCell>
2728 <mxCell id="c3" value="&lt;b&gt;★ only API of the UI&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="672.0" y="88" width="158.0" height="24" as="geometry"/></mxCell>
2829 <mxCell id="c4" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;main.go (root)&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;flags · Wails app · window&lt;br&gt;events · file dialog&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="1150" y="100" width="230" height="115" as="geometry"/></mxCell>
2930 <mxCell id="c6" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;bindings · events&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="385.0" y="145.0" width="130" height="24" as="geometry"/></mxCell>
3031 <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;creates&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="975.0" y="145.0" width="70" height="24" as="geometry"/></mxCell>
31- <mxCell id="c9" value="💬&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/channels&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;channels · direct · routing @&lt;br&gt;unseen context · queue · perms&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="300" width="230" height="110" as="geometry"/></mxCell>
32- <mxCell id="c10" value="🤖&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/agents&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;lazy start · LRU limit&lt;br&gt;idle stop · states · logs&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="300" width="230" height="110" as="geometry"/></mxCell>
33- <mxCell id="c11" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/config&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;load · validate · locate&lt;br&gt;default · save · watch&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="300" width="230" height="110" as="geometry"/></mxCell>
34- <mxCell id="c12" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/prefs&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;last opened file&lt;br&gt;recent files (JSON)&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="300" width="230" height="110" as="geometry"/></mxCell>
35- <mxCell id="c13" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/workspace&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;files · watch · terminals&lt;br&gt;local (pty) · docker exec&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1150" y="300" width="230" height="110" as="geometry"/></mxCell>
36- <mxCell id="c21" value="📥&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/store&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Pebble · one DB per config&lt;br&gt;channels · members · messages&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="490" width="230" height="105" as="geometry"/></mxCell>
37- <mxCell id="c22" value="🔧&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/acp&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;ACP v1 client · sessions&lt;br&gt;stdin → TERM → KILL&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="490" width="230" height="105" as="geometry"/></mxCell>
38- <mxCell id="c23" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;schema&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;config JSON Schema&lt;br&gt;ValidateConfig()&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="490" width="230" height="105" as="geometry"/></mxCell>
39- <mxCell id="c24" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/events&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;event names + payloads&lt;br&gt;Emitter interface&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="490" width="230" height="105" as="geometry"/></mxCell>
40- <mxCell id="c29" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;emits&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="438.0" width="60" height="24" as="geometry"/></mxCell>
41- <mxCell id="c30" value="&lt;b&gt;cockroachdb/pebble/v2&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="30" y="670" width="230" height="50" as="geometry"/></mxCell>
42- <mxCell id="c31" value="&lt;b&gt;acp-go-sdk → mm -acp&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="310" y="670" width="230" height="50" as="geometry"/></mxCell>
43- <mxCell id="c32" value="&lt;b&gt;jsonschema · yaml · fsnotify&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="590" y="670" width="230" height="50" as="geometry"/></mxCell>
44- <mxCell id="c33" value="&lt;b&gt;Wails v3 (main.go only)&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="870" y="670" width="230" height="50" as="geometry"/></mxCell>
45- <mxCell id="c34" value="&lt;b&gt;creack/pty · docker CLI&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="1150" y="490" width="230" height="50" as="geometry"/></mxCell>
46- <mxCell id="c38" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;stdio&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="445.0" y="620.0" width="50" height="24" as="geometry"/></mxCell>
47- <mxCell id="c40" value="channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="228.375" y="752.5" width="953.25" height="18.5" as="geometry"/></mxCell>
32+ <mxCell id="c9" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/shellpath&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;PATH of the user's shell&lt;br&gt;for apps opened from the Finder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1430" y="100" width="230" height="115" as="geometry"/></mxCell>
33+ <mxCell id="c11" value="💬&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/channels&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;channels · direct · routing @&lt;br&gt;unseen context · queue · perms&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="300" width="230" height="110" as="geometry"/></mxCell>
34+ <mxCell id="c12" value="🤖&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/agents&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;lazy start · LRU limit&lt;br&gt;idle stop · states · logs&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="300" width="230" height="110" as="geometry"/></mxCell>
35+ <mxCell id="c13" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/config&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;load · validate · locate&lt;br&gt;default · save · watch&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="300" width="230" height="110" as="geometry"/></mxCell>
36+ <mxCell id="c14" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/prefs&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;last opened file&lt;br&gt;recent files (JSON)&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="300" width="230" height="110" as="geometry"/></mxCell>
37+ <mxCell id="c15" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/workspace&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;files · watch · terminals&lt;br&gt;local (pty) · docker exec&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1150" y="300" width="230" height="110" as="geometry"/></mxCell>
38+ <mxCell id="c23" value="📥&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/store&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Pebble · one DB per config&lt;br&gt;channels · members · messages&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="490" width="230" height="105" as="geometry"/></mxCell>
39+ <mxCell id="c24" value="🔧&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/acp&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;ACP v1 client · sessions&lt;br&gt;stdin → TERM → KILL&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="490" width="230" height="105" as="geometry"/></mxCell>
40+ <mxCell id="c25" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;schema&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;config JSON Schema&lt;br&gt;ValidateConfig()&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="490" width="230" height="105" as="geometry"/></mxCell>
41+ <mxCell id="c26" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/events&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;event names + payloads&lt;br&gt;Emitter interface&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="490" width="230" height="105" as="geometry"/></mxCell>
42+ <mxCell id="c31" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;emits&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="438.0" width="60" height="24" as="geometry"/></mxCell>
43+ <mxCell id="c32" value="&lt;b&gt;cockroachdb/pebble/v2&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="30" y="670" width="230" height="50" as="geometry"/></mxCell>
44+ <mxCell id="c33" value="&lt;b&gt;acp-go-sdk → mm -acp&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="310" y="670" width="230" height="50" as="geometry"/></mxCell>
45+ <mxCell id="c34" value="&lt;b&gt;jsonschema · yaml · fsnotify&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="590" y="670" width="230" height="50" as="geometry"/></mxCell>
46+ <mxCell id="c35" value="&lt;b&gt;Wails v3 (main.go only)&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="870" y="670" width="230" height="50" as="geometry"/></mxCell>
47+ <mxCell id="c36" value="&lt;b&gt;creack/pty · docker CLI&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="1150" y="490" width="230" height="50" as="geometry"/></mxCell>
48+ <mxCell id="c40" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;stdio&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="445.0" y="620.0" width="50" height="24" as="geometry"/></mxCell>
49+ <mxCell id="c42" value="channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="368.375" y="752.5" width="953.25" height="18.5" as="geometry"/></mxCell>
4850 </root>
4951 </mxGraphModel>
5052 </diagram>
@@ -1,50 +1,52 @@
1 <mxfile host="app.diagrams.net">1 <mxfile host="app.diagrams.net">
2 <diagram id="packages" name="Sidekicks — packages and dependencies">2 <diagram id="packages" name="Sidekicks — packages and dependencies">
3- <mxGraphModel dx="1410" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1410" pageHeight="800" background="#F5F7FC" math="0" shadow="0">3+ <mxGraphModel dx="1690" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1690" pageHeight="800" background="#F5F7FC" math="0" shadow="0">
4 <root>4 <root>
5 <mxCell id="0"/>5 <mxCell id="0"/>
6 <mxCell id="1" parent="0"/>6 <mxCell id="1" parent="0"/>
7- <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;Sidekicks — packages and dependencies&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;arrows are imports · all packages exist (M4) · only main.go imports Wails&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="255.0" y="12" width="900" height="56" as="geometry"/></mxCell>7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;Sidekicks — packages and dependencies&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;arrows are imports · all packages exist (v0.1) · only main.go imports Wails&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="395.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8 <mxCell id="c5" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=block;startFill=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="157" as="sourcePoint"/><mxPoint x="590" y="157" as="targetPoint"/></mxGeometry></mxCell>8 <mxCell id="c5" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=block;startFill=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="157" as="sourcePoint"/><mxPoint x="590" y="157" as="targetPoint"/></mxGeometry></mxCell>
9 <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1150" y="157" as="sourcePoint"/><mxPoint x="820" y="157" as="targetPoint"/></mxGeometry></mxCell>9 <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1150" y="157" as="sourcePoint"/><mxPoint x="820" y="157" as="targetPoint"/></mxGeometry></mxCell>
10- <mxCell id="c14" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="145" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="145" y="255"/></Array></mxGeometry></mxCell>10+ <mxCell id="c10" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1380" y="157" as="sourcePoint"/><mxPoint x="1430" y="157" as="targetPoint"/></mxGeometry></mxCell>
11- <mxCell id="c15" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="425" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="425" y="255"/></Array></mxGeometry></mxCell>11+ <mxCell id="c16" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="145" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="145" y="255"/></Array></mxGeometry></mxCell>
12- <mxCell id="c16" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="705" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="705" y="255"/></Array></mxGeometry></mxCell>12+ <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="425" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="425" y="255"/></Array></mxGeometry></mxCell>
13- <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="985" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="985" y="255"/></Array></mxGeometry></mxCell>13+ <mxCell id="c18" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="705" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="705" y="255"/></Array></mxGeometry></mxCell>
14- <mxCell id="c18" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="1265" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="1265" y="255"/></Array></mxGeometry></mxCell>14+ <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="985" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="985" y="255"/></Array></mxGeometry></mxCell>
15- <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="355" as="sourcePoint"/><mxPoint x="310" y="355" as="targetPoint"/></mxGeometry></mxCell>15+ <mxCell id="c20" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="215" as="sourcePoint"/><mxPoint x="1265" y="300" as="targetPoint"/><Array as="points"><mxPoint x="705" y="255"/><mxPoint x="1265" y="255"/></Array></mxGeometry></mxCell>
16- <mxCell id="c20" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="540" y="355" as="sourcePoint"/><mxPoint x="590" y="355" as="targetPoint"/></mxGeometry></mxCell>16+ <mxCell id="c21" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="260" y="355" as="sourcePoint"/><mxPoint x="310" y="355" as="targetPoint"/></mxGeometry></mxCell>
17- <mxCell id="c25" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="410" as="sourcePoint"/><mxPoint x="145" y="490" as="targetPoint"/></mxGeometry></mxCell>17+ <mxCell id="c22" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="540" y="355" as="sourcePoint"/><mxPoint x="590" y="355" as="targetPoint"/></mxGeometry></mxCell>
18- <mxCell id="c26" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="410" as="sourcePoint"/><mxPoint x="425" y="490" as="targetPoint"/></mxGeometry></mxCell>18+ <mxCell id="c27" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="410" as="sourcePoint"/><mxPoint x="145" y="490" as="targetPoint"/></mxGeometry></mxCell>
19- <mxCell id="c27" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="410" as="sourcePoint"/><mxPoint x="705" y="490" as="targetPoint"/></mxGeometry></mxCell>19+ <mxCell id="c28" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="410" as="sourcePoint"/><mxPoint x="425" y="490" as="targetPoint"/></mxGeometry></mxCell>
20- <mxCell id="c28" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="520" y="410" as="sourcePoint"/><mxPoint x="985" y="490" as="targetPoint"/><Array as="points"><mxPoint x="520" y="450"/><mxPoint x="985" y="450"/></Array></mxGeometry></mxCell>20+ <mxCell id="c29" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="410" as="sourcePoint"/><mxPoint x="705" y="490" as="targetPoint"/></mxGeometry></mxCell>
21- <mxCell id="c35" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1265" y="410" as="sourcePoint"/><mxPoint x="1265" y="490" as="targetPoint"/></mxGeometry></mxCell>21+ <mxCell id="c30" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="520" y="410" as="sourcePoint"/><mxPoint x="985" y="490" as="targetPoint"/><Array as="points"><mxPoint x="520" y="450"/><mxPoint x="985" y="450"/></Array></mxGeometry></mxCell>
22- <mxCell id="c36" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="595" as="sourcePoint"/><mxPoint x="145" y="670" as="targetPoint"/></mxGeometry></mxCell>22+ <mxCell id="c37" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1265" y="410" as="sourcePoint"/><mxPoint x="1265" y="490" as="targetPoint"/></mxGeometry></mxCell>
23- <mxCell id="c37" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="595" as="sourcePoint"/><mxPoint x="425" y="670" as="targetPoint"/></mxGeometry></mxCell>23+ <mxCell id="c38" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="145" y="595" as="sourcePoint"/><mxPoint x="145" y="670" as="targetPoint"/></mxGeometry></mxCell>
24- <mxCell id="c39" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="595" as="sourcePoint"/><mxPoint x="705" y="670" as="targetPoint"/></mxGeometry></mxCell>24+ <mxCell id="c39" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="595" as="sourcePoint"/><mxPoint x="425" y="670" as="targetPoint"/></mxGeometry></mxCell>
25+ <mxCell id="c41" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="705" y="595" as="sourcePoint"/><mxPoint x="705" y="670" as="targetPoint"/></mxGeometry></mxCell>
25 <mxCell id="c1" value="💻&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;frontend/&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Svelte 5 · panels · chat · @ /&lt;br&gt;files · xterm.js · bindings&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="30" y="100" width="230" height="115" as="geometry"/></mxCell>26 <mxCell id="c1" value="💻&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;frontend/&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Svelte 5 · panels · chat · @ /&lt;br&gt;files · xterm.js · bindings&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="30" y="100" width="230" height="115" as="geometry"/></mxCell>
26 <mxCell id="c2" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/app&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Agent · Channel · Config&lt;br&gt;Workspace · Terminal&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="590" y="100" width="230" height="115" as="geometry"/></mxCell>27 <mxCell id="c2" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/app&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Agent · Channel · Config&lt;br&gt;Workspace · Terminal&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="590" y="100" width="230" height="115" as="geometry"/></mxCell>
27 <mxCell id="c3" value="&lt;b&gt;★ only API of the UI&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="672.0" y="88" width="158.0" height="24" as="geometry"/></mxCell>28 <mxCell id="c3" value="&lt;b&gt;★ only API of the UI&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="672.0" y="88" width="158.0" height="24" as="geometry"/></mxCell>
28 <mxCell id="c4" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;main.go (root)&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;flags · Wails app · window&lt;br&gt;events · file dialog&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="1150" y="100" width="230" height="115" as="geometry"/></mxCell>29 <mxCell id="c4" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;main.go (root)&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;flags · Wails app · window&lt;br&gt;events · file dialog&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="1150" y="100" width="230" height="115" as="geometry"/></mxCell>
29 <mxCell id="c6" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;bindings · events&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="385.0" y="145.0" width="130" height="24" as="geometry"/></mxCell>30 <mxCell id="c6" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;bindings · events&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="385.0" y="145.0" width="130" height="24" as="geometry"/></mxCell>
30 <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;creates&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="975.0" y="145.0" width="70" height="24" as="geometry"/></mxCell>31 <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;creates&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="975.0" y="145.0" width="70" height="24" as="geometry"/></mxCell>
31- <mxCell id="c9" value="💬&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/channels&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;channels · direct · routing @&lt;br&gt;unseen context · queue · perms&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="300" width="230" height="110" as="geometry"/></mxCell>32+ <mxCell id="c9" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/shellpath&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;PATH of the user's shell&lt;br&gt;for apps opened from the Finder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1430" y="100" width="230" height="115" as="geometry"/></mxCell>
32- <mxCell id="c10" value="🤖&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/agents&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;lazy start · LRU limit&lt;br&gt;idle stop · states · logs&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="300" width="230" height="110" as="geometry"/></mxCell>33+ <mxCell id="c11" value="💬&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/channels&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;channels · direct · routing @&lt;br&gt;unseen context · queue · perms&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="300" width="230" height="110" as="geometry"/></mxCell>
33- <mxCell id="c11" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/config&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;load · validate · locate&lt;br&gt;default · save · watch&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="300" width="230" height="110" as="geometry"/></mxCell>34+ <mxCell id="c12" value="🤖&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/agents&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;lazy start · LRU limit&lt;br&gt;idle stop · states · logs&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="300" width="230" height="110" as="geometry"/></mxCell>
34- <mxCell id="c12" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/prefs&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;last opened file&lt;br&gt;recent files (JSON)&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="300" width="230" height="110" as="geometry"/></mxCell>35+ <mxCell id="c13" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/config&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;load · validate · locate&lt;br&gt;default · save · watch&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="300" width="230" height="110" as="geometry"/></mxCell>
35- <mxCell id="c13" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/workspace&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;files · watch · terminals&lt;br&gt;local (pty) · docker exec&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1150" y="300" width="230" height="110" as="geometry"/></mxCell>36+ <mxCell id="c14" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/prefs&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;last opened file&lt;br&gt;recent files (JSON)&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="300" width="230" height="110" as="geometry"/></mxCell>
36- <mxCell id="c21" value="📥&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/store&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Pebble · one DB per config&lt;br&gt;channels · members · messages&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="490" width="230" height="105" as="geometry"/></mxCell>37+ <mxCell id="c15" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/workspace&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;files · watch · terminals&lt;br&gt;local (pty) · docker exec&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="1150" y="300" width="230" height="110" as="geometry"/></mxCell>
37- <mxCell id="c22" value="🔧&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/acp&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;ACP v1 client · sessions&lt;br&gt;stdin → TERM → KILL&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="490" width="230" height="105" as="geometry"/></mxCell>38+ <mxCell id="c23" value="📥&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/store&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Pebble · one DB per config&lt;br&gt;channels · members · messages&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="30" y="490" width="230" height="105" as="geometry"/></mxCell>
38- <mxCell id="c23" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;schema&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;config JSON Schema&lt;br&gt;ValidateConfig()&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="490" width="230" height="105" as="geometry"/></mxCell>39+ <mxCell id="c24" value="🔧&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/acp&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;ACP v1 client · sessions&lt;br&gt;stdin → TERM → KILL&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="310" y="490" width="230" height="105" as="geometry"/></mxCell>
39- <mxCell id="c24" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/events&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;event names + payloads&lt;br&gt;Emitter interface&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="490" width="230" height="105" as="geometry"/></mxCell>40+ <mxCell id="c25" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;schema&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;config JSON Schema&lt;br&gt;ValidateConfig()&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="590" y="490" width="230" height="105" as="geometry"/></mxCell>
40- <mxCell id="c29" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;emits&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="438.0" width="60" height="24" as="geometry"/></mxCell>41+ <mxCell id="c26" value="📋&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;internal/events&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;event names + payloads&lt;br&gt;Emitter interface&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="870" y="490" width="230" height="105" as="geometry"/></mxCell>
41- <mxCell id="c30" value="&lt;b&gt;cockroachdb/pebble/v2&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="30" y="670" width="230" height="50" as="geometry"/></mxCell>42+ <mxCell id="c31" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;emits&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="438.0" width="60" height="24" as="geometry"/></mxCell>
42- <mxCell id="c31" value="&lt;b&gt;acp-go-sdk → mm -acp&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="310" y="670" width="230" height="50" as="geometry"/></mxCell>43+ <mxCell id="c32" value="&lt;b&gt;cockroachdb/pebble/v2&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="30" y="670" width="230" height="50" as="geometry"/></mxCell>
43- <mxCell id="c32" value="&lt;b&gt;jsonschema · yaml · fsnotify&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="590" y="670" width="230" height="50" as="geometry"/></mxCell>44+ <mxCell id="c33" value="&lt;b&gt;acp-go-sdk → mm -acp&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="310" y="670" width="230" height="50" as="geometry"/></mxCell>
44- <mxCell id="c33" value="&lt;b&gt;Wails v3 (main.go only)&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="870" y="670" width="230" height="50" as="geometry"/></mxCell>45+ <mxCell id="c34" value="&lt;b&gt;jsonschema · yaml · fsnotify&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="590" y="670" width="230" height="50" as="geometry"/></mxCell>
45- <mxCell id="c34" value="&lt;b&gt;creack/pty · docker CLI&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="1150" y="490" width="230" height="50" as="geometry"/></mxCell>46+ <mxCell id="c35" value="&lt;b&gt;Wails v3 (main.go only)&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="870" y="670" width="230" height="50" as="geometry"/></mxCell>
46- <mxCell id="c38" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;stdio&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="445.0" y="620.0" width="50" height="24" as="geometry"/></mxCell>47+ <mxCell id="c36" value="&lt;b&gt;creack/pty · docker CLI&lt;/b&gt;" style="rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=12;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="1150" y="490" width="230" height="50" as="geometry"/></mxCell>
47- <mxCell id="c40" value="channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="228.375" y="752.5" width="953.25" height="18.5" as="geometry"/></mxCell>48+ <mxCell id="c40" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;stdio&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="445.0" y="620.0" width="50" height="24" as="geometry"/></mxCell>
49+ <mxCell id="c42" value="channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="368.375" y="752.5" width="953.25" height="18.5" as="geometry"/></mxCell>
48 </root>50 </root>
49 </mxGraphModel>51 </mxGraphModel>
50 </diagram>52 </diagram>
modified docs/diagrams/packages.png +0 -0
Binary files a/docs/diagrams/packages.png and b/docs/diagrams/packages.png differ
Binary files a/docs/diagrams/packages.png and b/docs/diagrams/packages.png differBinary files a/docs/diagrams/packages.png and b/docs/diagrams/packages.png differ
modified docs/diagrams/packages.svg +11 -5
@@ -1,4 +1,4 @@
1-<svg xmlns="http://www.w3.org/2000/svg" width="1410" height="800" viewBox="0 0 1410 800" font-family="DejaVu Sans, Verdana, sans-serif">
1+<svg xmlns="http://www.w3.org/2000/svg" width="1690" height="800" viewBox="0 0 1690 800" font-family="DejaVu Sans, Verdana, sans-serif">
22 <defs>
33 <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
44 <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
@@ -9,11 +9,12 @@
99 <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
1010 <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
1111 </defs>
12- <rect x="0" y="0" width="1410" height="800" fill="url(#gBg)"/>
13- <text x="705.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">Sidekicks — packages and dependencies</text>
14- <text x="705.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">arrows are imports · all packages exist (M4) · only main.go imports Wails</text>
12+ <rect x="0" y="0" width="1690" height="800" fill="url(#gBg)"/>
13+ <text x="845.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">Sidekicks — packages and dependencies</text>
14+ <text x="845.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">arrows are imports · all packages exist (v0.1) · only main.go imports Wails</text>
1515 <path d="M260,157 L590,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
1616 <path d="M1150,157 L820,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M1380,157 L1430,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
1718 <path d="M705,215 L705,255 L145,255 L145,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
1819 <path d="M705,215 L705,255 L425,255 L425,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
1920 <path d="M705,215 L705,255 L705,255 L705,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
@@ -50,6 +51,11 @@
5051 <text x="450" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">bindings · events</text>
5152 <rect x="975.0" y="145.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
5253 <text x="1010" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">creates</text>
54+ <g filter="url(#shadow)"><rect x="1430" y="100" width="230" height="115" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
55+ <text x="1545.0" y="137.5" text-anchor="middle" font-size="22">🧰</text>
56+ <text x="1545.0" y="160.5" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">internal/shellpath</text>
57+ <text x="1545.0" y="178.5" text-anchor="middle" font-size="11.5" fill="#01579B">PATH of the user's shell</text>
58+ <text x="1545.0" y="195.5" text-anchor="middle" font-size="11.5" fill="#01579B">for apps opened from the Finder</text>
5359 <g filter="url(#shadow)"><rect x="30" y="300" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
5460 <text x="145.0" y="335.0" text-anchor="middle" font-size="22">💬</text>
5561 <text x="145.0" y="358.0" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">internal/channels</text>
@@ -109,5 +115,5 @@
109115 <text x="1265.0" y="520.0" text-anchor="middle" font-size="12" font-weight="700" fill="#263238">creack/pty · docker CLI</text>
110116 <rect x="445.0" y="620.0" width="50" height="24" rx="5" fill="#FFFFFF"/>
111117 <text x="470" y="636.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">stdio</text>
112- <text x="705" y="765" text-anchor="middle" font-size="12.5" fill="#455A64" font-style="italic">channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.</text>
118+ <text x="845" y="765" text-anchor="middle" font-size="12.5" fill="#455A64" font-style="italic">channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.</text>
113119 </svg>
@@ -1,4 +1,4 @@
1-<svg xmlns="http://www.w3.org/2000/svg" width="1410" height="800" viewBox="0 0 1410 800" font-family="DejaVu Sans, Verdana, sans-serif">1+<svg xmlns="http://www.w3.org/2000/svg" width="1690" height="800" viewBox="0 0 1690 800" font-family="DejaVu Sans, Verdana, sans-serif">
2 <defs>2 <defs>
3 <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>3 <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4 <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>4 <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
@@ -9,11 +9,12 @@
9 <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>9 <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10 <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>10 <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11 </defs>11 </defs>
12- <rect x="0" y="0" width="1410" height="800" fill="url(#gBg)"/>12+ <rect x="0" y="0" width="1690" height="800" fill="url(#gBg)"/>
13- <text x="705.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">Sidekicks — packages and dependencies</text>13+ <text x="845.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">Sidekicks — packages and dependencies</text>
14- <text x="705.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">arrows are imports · all packages exist (M4) · only main.go imports Wails</text>14+ <text x="845.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">arrows are imports · all packages exist (v0.1) · only main.go imports Wails</text>
15 <path d="M260,157 L590,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>15 <path d="M260,157 L590,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
16 <path d="M1150,157 L820,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>16 <path d="M1150,157 L820,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M1380,157 L1430,157" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17 <path d="M705,215 L705,255 L145,255 L145,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>18 <path d="M705,215 L705,255 L145,255 L145,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
18 <path d="M705,215 L705,255 L425,255 L425,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>19 <path d="M705,215 L705,255 L425,255 L425,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
19 <path d="M705,215 L705,255 L705,255 L705,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>20 <path d="M705,215 L705,255 L705,255 L705,300" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
@@ -50,6 +51,11 @@
50 <text x="450" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">bindings · events</text>51 <text x="450" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">bindings · events</text>
51 <rect x="975.0" y="145.0" width="70" height="24" rx="5" fill="#FFFFFF"/>52 <rect x="975.0" y="145.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
52 <text x="1010" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">creates</text>53 <text x="1010" y="161.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">creates</text>
54+ <g filter="url(#shadow)"><rect x="1430" y="100" width="230" height="115" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
55+ <text x="1545.0" y="137.5" text-anchor="middle" font-size="22">🧰</text>
56+ <text x="1545.0" y="160.5" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">internal/shellpath</text>
57+ <text x="1545.0" y="178.5" text-anchor="middle" font-size="11.5" fill="#01579B">PATH of the user's shell</text>
58+ <text x="1545.0" y="195.5" text-anchor="middle" font-size="11.5" fill="#01579B">for apps opened from the Finder</text>
53 <g filter="url(#shadow)"><rect x="30" y="300" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>59 <g filter="url(#shadow)"><rect x="30" y="300" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
54 <text x="145.0" y="335.0" text-anchor="middle" font-size="22">💬</text>60 <text x="145.0" y="335.0" text-anchor="middle" font-size="22">💬</text>
55 <text x="145.0" y="358.0" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">internal/channels</text>61 <text x="145.0" y="358.0" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">internal/channels</text>
@@ -109,5 +115,5 @@
109 <text x="1265.0" y="520.0" text-anchor="middle" font-size="12" font-weight="700" fill="#263238">creack/pty · docker CLI</text>115 <text x="1265.0" y="520.0" text-anchor="middle" font-size="12" font-weight="700" fill="#263238">creack/pty · docker CLI</text>
110 <rect x="445.0" y="620.0" width="50" height="24" rx="5" fill="#FFFFFF"/>116 <rect x="445.0" y="620.0" width="50" height="24" rx="5" fill="#FFFFFF"/>
111 <text x="470" y="636.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">stdio</text>117 <text x="470" y="636.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">stdio</text>
112- <text x="705" y="765" text-anchor="middle" font-size="12.5" fill="#455A64" font-style="italic">channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.</text>118+ <text x="845" y="765" text-anchor="middle" font-size="12.5" fill="#455A64" font-style="italic">channels, workspace and app emit through internal/events; workspace reads config; app uses prefs, config, schema and store.</text>
113 </svg>119 </svg>
modified docs/en/README.md +4 -4
@@ -1,14 +1,14 @@
11 # Sidekicks documentation (English)
22
3-Sidekicks is a desktop application to talk with AI agents in channels, through the Agent Client Protocol, designed for people who are not developers. Milestone M4 (Agents' workspace) is done: besides channels with several agents (`@`, `/`, history kept across restarts), each agent's folder can be browsed and edited, and a terminal opens in it — on this computer or in a Docker container. Answers are formatted, tools need your approval, and the configuration can be edited from the window.
3+Sidekicks is a desktop application to talk with AI agents in channels, through the Agent Client Protocol, designed for people who are not developers. Version 0.1 is the first release: packages for macOS, Linux and Windows, an assistant that adds agents without writing YAML, error messages in plain words, keyboard shortcuts and a command palette — on top of channels with several agents (`@`, `/`, history kept across restarts), each agent's folder with an editor and a terminal (on this computer, in Docker or in an sbx sandbox), formatted answers and tools that need your approval.
44
55 This documentation follows [Diátaxis](https://diataxis.fr): pick the section that matches what you need right now.
66
77 | You want to… | Section | Pages |
88 | --- | --- | --- |
9-| **learn** by doing | Tutorials | [A channel with two agents](tutorials/first-channel.md) · [Your first conversation with an agent](tutorials/first-conversation.md) · [Your first launch of Sidekicks](tutorials/first-launch.md) · [Your first ACP exchange with a test agent](tutorials/first-acp-exchange.md) |
10-| **do** a specific task | How-to guides | [Work with an agent's files and terminal](how-to/work-with-an-agents-files.md) · [Run an agent in a Docker container](how-to/run-an-agent-in-docker.md) · [Run an agent in an sbx sandbox](how-to/run-an-agent-in-a-sandbox.md) · [Manage channels and their agents](how-to/manage-channels.md) · [Edit or switch the configuration](how-to/edit-or-switch-the-configuration.md) · [Reset Sidekicks](how-to/reset-sidekicks.md) · [Develop the application](how-to/develop-the-application.md) · [Run the tests](how-to/run-the-tests.md) · [Run a test agent](how-to/run-a-test-agent.md) · [Test milestone M4 on macOS](how-to/test-milestone-m4-on-macos.md) · [Test milestone M3 on macOS](how-to/test-milestone-m3-on-macos.md) · [Test milestone M2 on macOS](how-to/test-milestone-m2-on-macos.md) · [Test milestone M1 on macOS](how-to/test-milestone-m1-on-macos.md) · [Test milestone Initialize on macOS](how-to/test-milestone-initialize-on-macos.md) |
11-| **look up** an exact detail | Reference | [The configuration file](reference/configuration.md) · [Agents, channels and events](reference/agents-and-events.md) · [Commands](reference/commands.md) · [scripts/agents.sh](reference/agents-script.md) |
9+| **learn** by doing | Tutorials | [Your first agent, without writing any configuration](tutorials/your-first-agent.md) · [A channel with two agents](tutorials/first-channel.md) · [Your first conversation with an agent](tutorials/first-conversation.md) · [Your first launch of Sidekicks](tutorials/first-launch.md) · [Your first ACP exchange with a test agent](tutorials/first-acp-exchange.md) |
10+| **do** a specific task | How-to guides | [Install Sidekicks](how-to/install-sidekicks.md) · [Add an agent](how-to/add-an-agent.md) · [Work with an agent's files and terminal](how-to/work-with-an-agents-files.md) · [Run an agent in a Docker container](how-to/run-an-agent-in-docker.md) · [Run an agent in an sbx sandbox](how-to/run-an-agent-in-a-sandbox.md) · [Manage channels and their agents](how-to/manage-channels.md) · [Edit or switch the configuration](how-to/edit-or-switch-the-configuration.md) · [Reset Sidekicks](how-to/reset-sidekicks.md) · [Develop the application](how-to/develop-the-application.md) · [Run the tests](how-to/run-the-tests.md) · [Run a test agent](how-to/run-a-test-agent.md) · [Run the end-to-end scenarios (v0.1)](how-to/run-the-end-to-end-scenarios.md) · [Publish a release](how-to/publish-a-release.md) · [Test milestone M4 on macOS](how-to/test-milestone-m4-on-macos.md) · [Test milestone M3 on macOS](how-to/test-milestone-m3-on-macos.md) · [Test milestone M2 on macOS](how-to/test-milestone-m2-on-macos.md) · [Test milestone M1 on macOS](how-to/test-milestone-m1-on-macos.md) · [Test milestone Initialize on macOS](how-to/test-milestone-initialize-on-macos.md) |
11+| **look up** an exact detail | Reference | [The configuration file](reference/configuration.md) · [Keyboard shortcuts](reference/keyboard-shortcuts.md) · [Agents, channels and events](reference/agents-and-events.md) · [Commands](reference/commands.md) · [scripts/agents.sh](reference/agents-script.md) |
1212 | **understand** why | Explanation | [Workspaces and terminals](explanation/workspaces-and-terminals.md) · [Channels and what each agent knows](explanation/channels-and-context.md) · [Permissions and trust](explanation/permissions.md) · [Why ACP](explanation/why-acp.md) · [The interface stack](explanation/ui-stack.md) · [Architecture](explanation/architecture.md) · [Memory and local models](explanation/memory-and-local-models.md) |
1313
1414 ## Design records
@@ -1,14 +1,14 @@
1 # Sidekicks documentation (English)1 # Sidekicks documentation (English)
2 2
3-Sidekicks is a desktop application to talk with AI agents in channels, through the Agent Client Protocol, designed for people who are not developers. Milestone M4 (Agents' workspace) is done: besides channels with several agents (`@`, `/`, history kept across restarts), each agent's folder can be browsed and edited, and a terminal opens in it — on this computer or in a Docker container. Answers are formatted, tools need your approval, and the configuration can be edited from the window.3+Sidekicks is a desktop application to talk with AI agents in channels, through the Agent Client Protocol, designed for people who are not developers. Version 0.1 is the first release: packages for macOS, Linux and Windows, an assistant that adds agents without writing YAML, error messages in plain words, keyboard shortcuts and a command palette — on top of channels with several agents (`@`, `/`, history kept across restarts), each agent's folder with an editor and a terminal (on this computer, in Docker or in an sbx sandbox), formatted answers and tools that need your approval.
4 4
5 This documentation follows [Diátaxis](https://diataxis.fr): pick the section that matches what you need right now.5 This documentation follows [Diátaxis](https://diataxis.fr): pick the section that matches what you need right now.
6 6
7 | You want to… | Section | Pages |7 | You want to… | Section | Pages |
8 | --- | --- | --- |8 | --- | --- | --- |
9-| **learn** by doing | Tutorials | [A channel with two agents](tutorials/first-channel.md) · [Your first conversation with an agent](tutorials/first-conversation.md) · [Your first launch of Sidekicks](tutorials/first-launch.md) · [Your first ACP exchange with a test agent](tutorials/first-acp-exchange.md) |9+| **learn** by doing | Tutorials | [Your first agent, without writing any configuration](tutorials/your-first-agent.md) · [A channel with two agents](tutorials/first-channel.md) · [Your first conversation with an agent](tutorials/first-conversation.md) · [Your first launch of Sidekicks](tutorials/first-launch.md) · [Your first ACP exchange with a test agent](tutorials/first-acp-exchange.md) |
10-| **do** a specific task | How-to guides | [Work with an agent's files and terminal](how-to/work-with-an-agents-files.md) · [Run an agent in a Docker container](how-to/run-an-agent-in-docker.md) · [Run an agent in an sbx sandbox](how-to/run-an-agent-in-a-sandbox.md) · [Manage channels and their agents](how-to/manage-channels.md) · [Edit or switch the configuration](how-to/edit-or-switch-the-configuration.md) · [Reset Sidekicks](how-to/reset-sidekicks.md) · [Develop the application](how-to/develop-the-application.md) · [Run the tests](how-to/run-the-tests.md) · [Run a test agent](how-to/run-a-test-agent.md) · [Test milestone M4 on macOS](how-to/test-milestone-m4-on-macos.md) · [Test milestone M3 on macOS](how-to/test-milestone-m3-on-macos.md) · [Test milestone M2 on macOS](how-to/test-milestone-m2-on-macos.md) · [Test milestone M1 on macOS](how-to/test-milestone-m1-on-macos.md) · [Test milestone Initialize on macOS](how-to/test-milestone-initialize-on-macos.md) |10+| **do** a specific task | How-to guides | [Install Sidekicks](how-to/install-sidekicks.md) · [Add an agent](how-to/add-an-agent.md) · [Work with an agent's files and terminal](how-to/work-with-an-agents-files.md) · [Run an agent in a Docker container](how-to/run-an-agent-in-docker.md) · [Run an agent in an sbx sandbox](how-to/run-an-agent-in-a-sandbox.md) · [Manage channels and their agents](how-to/manage-channels.md) · [Edit or switch the configuration](how-to/edit-or-switch-the-configuration.md) · [Reset Sidekicks](how-to/reset-sidekicks.md) · [Develop the application](how-to/develop-the-application.md) · [Run the tests](how-to/run-the-tests.md) · [Run a test agent](how-to/run-a-test-agent.md) · [Run the end-to-end scenarios (v0.1)](how-to/run-the-end-to-end-scenarios.md) · [Publish a release](how-to/publish-a-release.md) · [Test milestone M4 on macOS](how-to/test-milestone-m4-on-macos.md) · [Test milestone M3 on macOS](how-to/test-milestone-m3-on-macos.md) · [Test milestone M2 on macOS](how-to/test-milestone-m2-on-macos.md) · [Test milestone M1 on macOS](how-to/test-milestone-m1-on-macos.md) · [Test milestone Initialize on macOS](how-to/test-milestone-initialize-on-macos.md) |
11-| **look up** an exact detail | Reference | [The configuration file](reference/configuration.md) · [Agents, channels and events](reference/agents-and-events.md) · [Commands](reference/commands.md) · [scripts/agents.sh](reference/agents-script.md) |11+| **look up** an exact detail | Reference | [The configuration file](reference/configuration.md) · [Keyboard shortcuts](reference/keyboard-shortcuts.md) · [Agents, channels and events](reference/agents-and-events.md) · [Commands](reference/commands.md) · [scripts/agents.sh](reference/agents-script.md) |
12 | **understand** why | Explanation | [Workspaces and terminals](explanation/workspaces-and-terminals.md) · [Channels and what each agent knows](explanation/channels-and-context.md) · [Permissions and trust](explanation/permissions.md) · [Why ACP](explanation/why-acp.md) · [The interface stack](explanation/ui-stack.md) · [Architecture](explanation/architecture.md) · [Memory and local models](explanation/memory-and-local-models.md) |12 | **understand** why | Explanation | [Workspaces and terminals](explanation/workspaces-and-terminals.md) · [Channels and what each agent knows](explanation/channels-and-context.md) · [Permissions and trust](explanation/permissions.md) · [Why ACP](explanation/why-acp.md) · [The interface stack](explanation/ui-stack.md) · [Architecture](explanation/architecture.md) · [Memory and local models](explanation/memory-and-local-models.md) |
13 13
14 ## Design records14 ## Design records
modified docs/en/explanation/architecture.md +9 -1
@@ -6,7 +6,7 @@ Before writing the application, the project fixed how its code is divided, what
66
77 ![Packages and dependencies](../../diagrams/packages.png)
88
9-The diagram's source is [packages.drawio](../../diagrams/packages.drawio). Blue boxes exist since milestone M1; grey ones are planned.
9+The diagram's source is [packages.drawio](../../diagrams/packages.drawio). Every box exists (v0.1).
1010
1111 ## Why this split
1212
@@ -30,6 +30,14 @@ Each configuration file gets its own database: switching from "my work agents" t
3030
3131 While an agent answers, the answer lives in memory and the interface receives each fragment. It is written to disk when the turn ends. Writing fifty times a second would wear the disk for nothing; the price is losing an unfinished answer if the application crashes, which is acceptable.
3232
33+## Programs are found as in a terminal
34+
35+An application opened from the Finder does not inherit the `PATH` of the user's terminal: macOS gives it `/usr/bin:/bin:/usr/sbin:/sbin`, so `mm`, `docker` or `sbx` installed with Homebrew or in `~/.local/bin` are "not found", although the user just ran them in a terminal. Asking a non-developer to write full paths would be the wrong answer. At startup, before the configuration is read, `main.go` asks the user's shell for its `PATH` (`internal/shellpath`: an interactive login shell, so that it reads the same files as a terminal, with a five-second limit) and puts it in front of its own. The agents inherit it, and the warnings of the configuration ("program not found") are computed with it. When the shell cannot be read, nothing changes and the full path remains the way out.
36+
37+## Errors in plain words
38+
39+Go, the agents, Docker and sbx report errors for developers (`exec: "mm": executable file not found in $PATH`). The Go side keeps them as they are: they are precise, the tests check them, and they are what a developer needs in the log. The interface translates the common ones (`frontend/src/lib/errors.ts`) into a sentence that says what is wrong and what to do, in the interface's language, and keeps the original text folded under **Technical details**. An unknown error is shown as it is rather than hidden behind a vague sentence.
40+
3341 ## Rejected alternatives
3442
3543 - **Storing conversations in the YAML file**: it would turn a file people edit into a database, and conflict with the application writing to it.
@@ -6,7 +6,7 @@ Before writing the application, the project fixed how its code is divided, what
6 6
7 ![Packages and dependencies](../../diagrams/packages.png)7 ![Packages and dependencies](../../diagrams/packages.png)
8 8
9-The diagram's source is [packages.drawio](../../diagrams/packages.drawio). Blue boxes exist since milestone M1; grey ones are planned.9+The diagram's source is [packages.drawio](../../diagrams/packages.drawio). Every box exists (v0.1).
10 10
11 ## Why this split11 ## Why this split
12 12
@@ -30,6 +30,14 @@ Each configuration file gets its own database: switching from "my work agents" t
30 30
31 While an agent answers, the answer lives in memory and the interface receives each fragment. It is written to disk when the turn ends. Writing fifty times a second would wear the disk for nothing; the price is losing an unfinished answer if the application crashes, which is acceptable.31 While an agent answers, the answer lives in memory and the interface receives each fragment. It is written to disk when the turn ends. Writing fifty times a second would wear the disk for nothing; the price is losing an unfinished answer if the application crashes, which is acceptable.
32 32
33+## Programs are found as in a terminal
34+
35+An application opened from the Finder does not inherit the `PATH` of the user's terminal: macOS gives it `/usr/bin:/bin:/usr/sbin:/sbin`, so `mm`, `docker` or `sbx` installed with Homebrew or in `~/.local/bin` are "not found", although the user just ran them in a terminal. Asking a non-developer to write full paths would be the wrong answer. At startup, before the configuration is read, `main.go` asks the user's shell for its `PATH` (`internal/shellpath`: an interactive login shell, so that it reads the same files as a terminal, with a five-second limit) and puts it in front of its own. The agents inherit it, and the warnings of the configuration ("program not found") are computed with it. When the shell cannot be read, nothing changes and the full path remains the way out.
36+
37+## Errors in plain words
38+
39+Go, the agents, Docker and sbx report errors for developers (`exec: "mm": executable file not found in $PATH`). The Go side keeps them as they are: they are precise, the tests check them, and they are what a developer needs in the log. The interface translates the common ones (`frontend/src/lib/errors.ts`) into a sentence that says what is wrong and what to do, in the interface's language, and keeps the original text folded under **Technical details**. An unknown error is shown as it is rather than hidden behind a vague sentence.
40+
33 ## Rejected alternatives41 ## Rejected alternatives
34 42
35 - **Storing conversations in the YAML file**: it would turn a file people edit into a database, and conflict with the application writing to it.43 - **Storing conversations in the YAML file**: it would turn a file people edit into a database, and conflict with the application writing to it.
added docs/en/how-to/add-an-agent.md +26 -0
new file mode 100644
@@ -0,0 +1,26 @@
1+# How to add an agent
2+
3+This guide shows how to add an agent from the Sidekicks window, with the "Add an agent" assistant, wherever the agent runs: on this computer, in a Docker container or in an sbx sandbox. It assumes the agent's program speaks ACP (for `mm`: the `-acp` argument).
4+
5+## Steps
6+
7+1. Open the assistant: **+ Add my first agent** on the welcome screen, **+ Add an agent** under the agents list, or **⇧⌘N** (Ctrl+Shift+N on Linux and Windows).
8+2. **Who is it?** Fill in **Name shown** (e.g. `Garfield (translator)`). **Name used with @** follows it; change it if you want a shorter one (lowercase letters, digits, `-` and `_`, starting with a letter). The icon and the description are optional.
9+3. **Where does it run?** Choose the environment, then fill in what it asks for (see below).
10+4. **How is it started?** Fill in **Program**, **Arguments** (on one line, as in a terminal; quotes group words) and **Agent's folder**. The folder is created when it does not exist.
11+5. Click **Add the agent**. Its direct conversation opens once the configuration is reloaded.
12+
13+When the agent cannot be added (a name already used, for instance), the assistant says why and writes nothing.
14+
15+## Variants
16+
17+- **On this computer**: the program runs here, in the agent's folder. If Sidekicks was opened from the Finder and does not find the program, write its full path (`which mm` in a terminal gives it). Sidekicks reads your shell's `PATH` at startup, so this is rarely needed.
18+- **In a Docker container**: give the **Container name**. The container must be running, with the agent's folder mounted at the same path, and contain the program: see [How to run an agent in a Docker container](run-an-agent-in-docker.md). Sidekicks starts the agent with `docker exec -i <container> <program> <arguments>`.
19+- **In an sbx sandbox**: give the **Sandbox name**. The sandbox must exist, with the agent's folder at the same path: see [How to run an agent in an sbx sandbox](run-an-agent-in-a-sandbox.md). Sidekicks starts the agent with `sbx exec -i <sandbox> <program> <arguments>`.
20+- **Options the assistant does not ask for** (`env`, `afterStop`, a different `workspacePath`): add the agent with the assistant, then complete its entry in **Settings**.
21+
22+## See also
23+
24+- What the assistant writes: [The configuration file](../reference/configuration.md)
25+- [How to edit or switch the configuration](edit-or-switch-the-configuration.md)
26+- [Tutorial: your first agent, without writing any configuration](../tutorials/your-first-agent.md)
new file mode 100644
@@ -0,0 +1,26 @@
1+# How to add an agent
2+
3+This guide shows how to add an agent from the Sidekicks window, with the "Add an agent" assistant, wherever the agent runs: on this computer, in a Docker container or in an sbx sandbox. It assumes the agent's program speaks ACP (for `mm`: the `-acp` argument).
4+
5+## Steps
6+
7+1. Open the assistant: **+ Add my first agent** on the welcome screen, **+ Add an agent** under the agents list, or **⇧⌘N** (Ctrl+Shift+N on Linux and Windows).
8+2. **Who is it?** Fill in **Name shown** (e.g. `Garfield (translator)`). **Name used with @** follows it; change it if you want a shorter one (lowercase letters, digits, `-` and `_`, starting with a letter). The icon and the description are optional.
9+3. **Where does it run?** Choose the environment, then fill in what it asks for (see below).
10+4. **How is it started?** Fill in **Program**, **Arguments** (on one line, as in a terminal; quotes group words) and **Agent's folder**. The folder is created when it does not exist.
11+5. Click **Add the agent**. Its direct conversation opens once the configuration is reloaded.
12+
13+When the agent cannot be added (a name already used, for instance), the assistant says why and writes nothing.
14+
15+## Variants
16+
17+- **On this computer**: the program runs here, in the agent's folder. If Sidekicks was opened from the Finder and does not find the program, write its full path (`which mm` in a terminal gives it). Sidekicks reads your shell's `PATH` at startup, so this is rarely needed.
18+- **In a Docker container**: give the **Container name**. The container must be running, with the agent's folder mounted at the same path, and contain the program: see [How to run an agent in a Docker container](run-an-agent-in-docker.md). Sidekicks starts the agent with `docker exec -i <container> <program> <arguments>`.
19+- **In an sbx sandbox**: give the **Sandbox name**. The sandbox must exist, with the agent's folder at the same path: see [How to run an agent in an sbx sandbox](run-an-agent-in-a-sandbox.md). Sidekicks starts the agent with `sbx exec -i <sandbox> <program> <arguments>`.
20+- **Options the assistant does not ask for** (`env`, `afterStop`, a different `workspacePath`): add the agent with the assistant, then complete its entry in **Settings**.
21+
22+## See also
23+
24+- What the assistant writes: [The configuration file](../reference/configuration.md)
25+- [How to edit or switch the configuration](edit-or-switch-the-configuration.md)
26+- [Tutorial: your first agent, without writing any configuration](../tutorials/your-first-agent.md)
modified docs/en/how-to/develop-the-application.md +2 -0
@@ -30,6 +30,8 @@ make build # bin/sidekicks
3030 make package # macOS: bin/sidekicks.app, signed ad hoc
3131 ```
3232
33+Release packages go to `dist/`: `make package-macos`, `make package-linux`, `make package-windows` (see [How to publish a release](publish-a-release.md)).
34+
3335 ## When you change…
3436
3537 - **a Go service or an event payload** (`internal/app`, `internal/events`, `main.go`): run `make bindings` and commit `frontend/bindings` — the CI fails when they are stale.
@@ -30,6 +30,8 @@ make build # bin/sidekicks
30 make package # macOS: bin/sidekicks.app, signed ad hoc30 make package # macOS: bin/sidekicks.app, signed ad hoc
31 ```31 ```
32 32
33+Release packages go to `dist/`: `make package-macos`, `make package-linux`, `make package-windows` (see [How to publish a release](publish-a-release.md)).
34+
33 ## When you change…35 ## When you change…
34 36
35 - **a Go service or an event payload** (`internal/app`, `internal/events`, `main.go`): run `make bindings` and commit `frontend/bindings` — the CI fails when they are stale.37 - **a Go service or an event payload** (`internal/app`, `internal/events`, `main.go`): run `make bindings` and commit `frontend/bindings` — the CI fails when they are stale.
added docs/en/how-to/install-sidekicks.md +48 -0
new file mode 100644
@@ -0,0 +1,48 @@
1+# How to install Sidekicks
2+
3+This guide shows how to install Sidekicks from the packages of a release, on macOS, Linux or Windows. The packages are on the project's releases page, `https://rickub.com/bots-garden/sidekicks/releases`; `SHA256SUMS` lists their checksums.
4+
5+Sidekicks talks with agents; it does not contain one. Install your agent's program too (for instance `mm`).
6+
7+## macOS (Apple Silicon and Intel)
8+
9+Sidekicks needs macOS 13 (Ventura) or later.
10+
11+1. Download `Sidekicks-<version>-macos-universal.dmg` and open it.
12+2. Drag **sidekicks** into **Applications**.
13+3. Open it once from **Applications**. The application is not signed by an Apple developer account, so macOS refuses it the first time ("Apple could not verify…").
14+4. Open **System Settings → Privacy & Security**, scroll to the message about Sidekicks and click **Open Anyway**, then confirm.
15+
16+From then on, Sidekicks opens normally.
17+
18+## Linux (x86-64)
19+
20+Sidekicks needs GTK 4 and WebKitGTK 6 (Ubuntu 24.04 or later, Debian 13, Fedora 40 or later).
21+
22+- **Debian, Ubuntu**: `sudo apt install ./sidekicks_<version>_linux_amd64.deb`
23+- **Fedora**: `sudo dnf install ./sidekicks_<version>_linux_amd64.rpm`
24+- **Any distribution, without installing**: `chmod +x Sidekicks-<version>-x86_64.AppImage`, then run it. If it complains about FUSE, run it with `--appimage-extract-and-run`.
25+- **The bare program**: `sidekicks_<version>_linux_amd64.tar.gz` holds `sidekicks`; the GTK 4 and WebKitGTK 6 libraries must be installed.
26+
27+The `.deb` and `.rpm` add Sidekicks to the applications menu.
28+
29+## Windows (x86-64)
30+
31+1. Download `Sidekicks-<version>-windows-amd64-installer.exe` and run it.
32+2. The installer is not signed: Windows SmartScreen may say "Windows protected your PC". Click **More info**, then **Run anyway**.
33+3. The installer adds Microsoft Edge WebView2 when it is missing, then a Sidekicks shortcut.
34+
35+`sidekicks_<version>_windows_amd64.zip` holds the bare `sidekicks.exe`, for a machine where WebView2 is already present.
36+
37+On Windows, the **Terminal** tab of an agent is not available yet; files, conversations and agents in Docker work.
38+
39+## Variants
40+
41+- **Check a download**: `shasum -a 256 <file>` (macOS) or `sha256sum <file>` (Linux), and compare with `SHA256SUMS`.
42+- **macOS, from a terminal**: `xattr -dr com.apple.quarantine /Applications/sidekicks.app` lifts the quarantine instead of step 4.
43+- **Build it yourself**: `make package-macos`, `make package-linux` or `make package-windows` (see [How to publish a release](publish-a-release.md)).
44+
45+## See also
46+
47+- [Tutorial: your first agent, without writing any configuration](../tutorials/your-first-agent.md)
48+- [How to reset Sidekicks](reset-sidekicks.md)
new file mode 100644
@@ -0,0 +1,48 @@
1+# How to install Sidekicks
2+
3+This guide shows how to install Sidekicks from the packages of a release, on macOS, Linux or Windows. The packages are on the project's releases page, `https://rickub.com/bots-garden/sidekicks/releases`; `SHA256SUMS` lists their checksums.
4+
5+Sidekicks talks with agents; it does not contain one. Install your agent's program too (for instance `mm`).
6+
7+## macOS (Apple Silicon and Intel)
8+
9+Sidekicks needs macOS 13 (Ventura) or later.
10+
11+1. Download `Sidekicks-<version>-macos-universal.dmg` and open it.
12+2. Drag **sidekicks** into **Applications**.
13+3. Open it once from **Applications**. The application is not signed by an Apple developer account, so macOS refuses it the first time ("Apple could not verify…").
14+4. Open **System Settings → Privacy & Security**, scroll to the message about Sidekicks and click **Open Anyway**, then confirm.
15+
16+From then on, Sidekicks opens normally.
17+
18+## Linux (x86-64)
19+
20+Sidekicks needs GTK 4 and WebKitGTK 6 (Ubuntu 24.04 or later, Debian 13, Fedora 40 or later).
21+
22+- **Debian, Ubuntu**: `sudo apt install ./sidekicks_<version>_linux_amd64.deb`
23+- **Fedora**: `sudo dnf install ./sidekicks_<version>_linux_amd64.rpm`
24+- **Any distribution, without installing**: `chmod +x Sidekicks-<version>-x86_64.AppImage`, then run it. If it complains about FUSE, run it with `--appimage-extract-and-run`.
25+- **The bare program**: `sidekicks_<version>_linux_amd64.tar.gz` holds `sidekicks`; the GTK 4 and WebKitGTK 6 libraries must be installed.
26+
27+The `.deb` and `.rpm` add Sidekicks to the applications menu.
28+
29+## Windows (x86-64)
30+
31+1. Download `Sidekicks-<version>-windows-amd64-installer.exe` and run it.
32+2. The installer is not signed: Windows SmartScreen may say "Windows protected your PC". Click **More info**, then **Run anyway**.
33+3. The installer adds Microsoft Edge WebView2 when it is missing, then a Sidekicks shortcut.
34+
35+`sidekicks_<version>_windows_amd64.zip` holds the bare `sidekicks.exe`, for a machine where WebView2 is already present.
36+
37+On Windows, the **Terminal** tab of an agent is not available yet; files, conversations and agents in Docker work.
38+
39+## Variants
40+
41+- **Check a download**: `shasum -a 256 <file>` (macOS) or `sha256sum <file>` (Linux), and compare with `SHA256SUMS`.
42+- **macOS, from a terminal**: `xattr -dr com.apple.quarantine /Applications/sidekicks.app` lifts the quarantine instead of step 4.
43+- **Build it yourself**: `make package-macos`, `make package-linux` or `make package-windows` (see [How to publish a release](publish-a-release.md)).
44+
45+## See also
46+
47+- [Tutorial: your first agent, without writing any configuration](../tutorials/your-first-agent.md)
48+- [How to reset Sidekicks](reset-sidekicks.md)
added docs/en/how-to/publish-a-release.md +23 -0
new file mode 100644
@@ -0,0 +1,23 @@
1+# How to publish a release
2+
3+This guide shows how to publish a Sidekicks release from a Mac: build every package, tag, attach the files to the release that Rickub creates. It assumes the development tools of [How to develop the application](develop-the-application.md), Docker Desktop running, `brew install makensis zip`, and the right to push tags. [RELEASING.md](../../../RELEASING.md) is the same procedure as a checklist.
4+
5+## Steps
6+
7+1. **Set the version** in `build/config.yml` (`info.version`), run `wails3 task common:update:build-assets`, put `LSMinimumSystemVersion` back to `13.0.0` in `build/darwin/Info.plist` and `Info.dev.plist`, then commit, merge into `main` and `git checkout main && git pull`.
8+2. **Build everything**: `make release-build`. `dist/` then holds the macOS packages (universal `.dmg` and `.zip`), the Windows ones (installer and `.zip`), the Linux amd64 ones (`.deb`, `.rpm`, `.AppImage`, `.tar.gz`, built in Docker) and `SHA256SUMS`.
9+3. **Tag**: `make release-tag`. The tag `v<version>` is pushed, and the **Release** workflow creates the release on Rickub with notes listing the files.
10+4. **Attach the files**: on Rickub, open **Releases → v<version>** and attach every file of `dist/`.
11+
12+## Variants
13+
14+- **Linux arm64 too**: `LINUX_ARCHES="amd64 arm64" make release-build`.
15+- **Leave a system out**: `SKIP="linux"` (or `macos`, `windows`, several separated by spaces) before `make release-build`.
16+- **Tag without pushing**: `scripts/tag-release.sh --no-push`, then `git push origin v<version>` when ready.
17+- **Wrong tag**: `git push origin :refs/tags/v<version>` and `git tag -d v<version>`, delete the release if it was created, fix the version and tag again.
18+- **One package only**: `make package-macos`, `make package-windows` or `make package-linux` (Linux only, for the machine's architecture).
19+
20+## See also
21+
22+- [How to install Sidekicks](install-sidekicks.md)
23+- [Reference: commands](../reference/commands.md) — `make release-*`, `scripts/build-release.sh`, `scripts/tag-release.sh`, the Release workflow
new file mode 100644
@@ -0,0 +1,23 @@
1+# How to publish a release
2+
3+This guide shows how to publish a Sidekicks release from a Mac: build every package, tag, attach the files to the release that Rickub creates. It assumes the development tools of [How to develop the application](develop-the-application.md), Docker Desktop running, `brew install makensis zip`, and the right to push tags. [RELEASING.md](../../../RELEASING.md) is the same procedure as a checklist.
4+
5+## Steps
6+
7+1. **Set the version** in `build/config.yml` (`info.version`), run `wails3 task common:update:build-assets`, put `LSMinimumSystemVersion` back to `13.0.0` in `build/darwin/Info.plist` and `Info.dev.plist`, then commit, merge into `main` and `git checkout main && git pull`.
8+2. **Build everything**: `make release-build`. `dist/` then holds the macOS packages (universal `.dmg` and `.zip`), the Windows ones (installer and `.zip`), the Linux amd64 ones (`.deb`, `.rpm`, `.AppImage`, `.tar.gz`, built in Docker) and `SHA256SUMS`.
9+3. **Tag**: `make release-tag`. The tag `v<version>` is pushed, and the **Release** workflow creates the release on Rickub with notes listing the files.
10+4. **Attach the files**: on Rickub, open **Releases → v<version>** and attach every file of `dist/`.
11+
12+## Variants
13+
14+- **Linux arm64 too**: `LINUX_ARCHES="amd64 arm64" make release-build`.
15+- **Leave a system out**: `SKIP="linux"` (or `macos`, `windows`, several separated by spaces) before `make release-build`.
16+- **Tag without pushing**: `scripts/tag-release.sh --no-push`, then `git push origin v<version>` when ready.
17+- **Wrong tag**: `git push origin :refs/tags/v<version>` and `git tag -d v<version>`, delete the release if it was created, fix the version and tag again.
18+- **One package only**: `make package-macos`, `make package-windows` or `make package-linux` (Linux only, for the machine's architecture).
19+
20+## See also
21+
22+- [How to install Sidekicks](install-sidekicks.md)
23+- [Reference: commands](../reference/commands.md) — `make release-*`, `scripts/build-release.sh`, `scripts/tag-release.sh`, the Release workflow
added docs/en/how-to/run-the-end-to-end-scenarios.md +100 -0
new file mode 100644
@@ -0,0 +1,100 @@
1+# How to run the end-to-end scenarios (v0.1)
2+
3+This guide runs the six scenarios that validate a release with the real test agents (issue #34), then the checks of what v0.1 added. It assumes a Mac with `mm`, llmman running, the models of the four test agents pulled, and Sidekicks built from the release's tag (`make package-macos`, or `make dev`).
4+
5+Two agents at most run at once (`limits.maxRunningAgents: 2`). Between two scenarios, unload every model, so each scenario starts cold:
6+
7+```bash
8+scripts/agents.sh unload --all
9+scripts/agents.sh ps # no model loaded
10+```
11+
12+## 0. Prepare
13+
14+1. `make install-test-config` (the four test agents at the default location; the previous file is kept as a backup).
15+2. Open Sidekicks. Under **AGENTS**: Alice, Bob, Riker, Garfield, all "stopped".
16+3. Open the [report](#report) below in a text editor, to fill it in as you go.
17+
18+## 1. Alice — a simple conversation, Markdown, cancelling
19+
20+1. Click **Alice (assistant)**, send: `Donne un tableau markdown de 2 lignes (nom, rôle) puis un bloc de code Go qui affiche hello.`
21+2. Expected: the answer streams; a table and coloured Go code with **Copy** appear.
22+3. Send: `Écris un poème de 40 vers sur la mer.` and click **■ Stop** after a few lines.
23+4. Expected: the answer stops and ends with "Answer stopped."; Alice goes back to "ready".
24+
25+## 2. Alice + Bob — a multi-agent channel, routing, shared context
26+
27+1. Create a channel `#e2e` (**⌘N**). Send: `@alice donne un prénom pour un chat roux.`
28+2. Expected: "Alice (assistant) joined the channel." then her answer with a name.
29+3. Send: `@bob que penses-tu du prénom proposé par Alice ?`
30+4. Expected: Bob joins and quotes the name Alice gave: he received the messages he had not seen.
31+5. Send a message without any mention: expected, the last agent who spoke (Bob) answers.
32+
33+## 3. Riker — tool calls, permissions
34+
35+1. Unload the models. Click **Riker (tools)**, send: `Crée le fichier e2e.txt contenant "ok", puis liste le dossier.`
36+2. Expected: a permission card in plain words. Click **Allow once**: the tool card turns ✓, `e2e.txt` is listed.
37+3. Send: `Supprime e2e.txt.` Click **Reject once**.
38+4. Expected: Riker says it could not; the file is still there (📂 → Files).
39+
40+## 4. Garfield — slash commands and skills
41+
42+1. Unload the models. Click **Garfield (translator)**, type `/`.
43+2. Expected: "Asking Garfield (translator) for their commands…" then Garfield's commands and skills (`idioms`, `styles`…).
44+3. Pick `/idioms` (Tab), add `il pleut des cordes`, send.
45+4. Expected: the answer explains the idiom with an English equivalent.
46+
47+## 5. Garfield + Bob — `/garfield:command` in a channel
48+
49+1. Unload the models. In `#e2e`, invite Garfield (**+ Invite**), then type `/gar`.
50+2. Expected: `/garfield:idioms`, `/garfield:styles`… listed with their agent.
51+3. Send `/garfield:styles Bonjour à tous`. Expected: only Garfield answers.
52+4. Send `@bob traduis la première version de Garfield en espagnol.` Expected: Bob answers from Garfield's text.
53+
54+## 6. Alice — restart and resume
55+
56+1. Quit Sidekicks (⌘Q) while nothing is answering. Reopen it.
57+2. Expected: `#e2e` and the direct conversations are there with their messages; the agents are "stopped".
58+3. In Alice's conversation, send: `Quelle était ma première question ?`
59+4. Expected: Alice answers from the earlier exchange (her session was resumed, or the history was given back to her).
60+
61+## 7. What v0.1 added
62+
63+| # | Check | Expected |
64+| --- | --- | --- |
65+| 7.1 | `mv ~/Library/Application\ Support/sidekicks ~/sidekicks-backup`, open Sidekicks | Welcome screen: logo, three steps, **+ Add my first agent** |
66+| 7.2 | Add Alice with the assistant (program `mm`, arguments `-acp -config agent.yaml`, folder `agents/alice`) | Her conversation opens; Settings shows her entry, the file's comments are kept |
67+| 7.3 | Add an agent whose program is `nothere`, start it (▶) | "The program "nothere" cannot be found…", with **Technical details** folded |
68+| 7.4 | Add an agent in a container that does not exist, start it | A sentence about the missing container |
69+| 7.5 | ⌘K, type `ali`, Enter; ⌘N; ⌘L; ⌘,; ⌘J; ⌘B; ⇧⌘N | Each does what [Keyboard shortcuts](../reference/keyboard-shortcuts.md) says |
70+| 7.6 | Tab through the window | The focused element is always outlined |
71+| 7.7 | Open the `.app` installed from the `.dmg` **from the Finder**, start Alice | It starts: `mm` is found without a full path (the shell's PATH) |
72+| 7.8 | Put the backup back: `rm -r ~/Library/Application\ Support/sidekicks && mv ~/sidekicks-backup ~/Library/Application\ Support/sidekicks` | Your agents and history are back |
73+
74+## Report
75+
76+Copy this into a comment of issue #34 and fill it in:
77+
78+```markdown
79+## End-to-end report — Sidekicks <version>, <date>
80+
81+Mac: <model, RAM>, macOS <version> · mm <version> · build: <dmg | make dev>
82+
83+| # | Scenario | Result | Notes |
84+| --- | --- | --- | --- |
85+| 1 | Alice: conversation, Markdown, cancel | ✅ / ❌ | |
86+| 2 | Alice + Bob: channel, routing, shared context | ✅ / ❌ | |
87+| 3 | Riker: tools, allow / reject | ✅ / ❌ | |
88+| 4 | Garfield: / commands and skills | ✅ / ❌ | |
89+| 5 | Garfield + Bob: /garfield:command in a channel | ✅ / ❌ | |
90+| 6 | Alice: restart and resume | ✅ / ❌ | |
91+| 7 | v0.1 checks (7.1–7.8) | ✅ / ❌ | |
92+
93+Models unloaded between scenarios: yes / no
94+```
95+
96+## See also
97+
98+- [How to run the tests](run-the-tests.md) — the automated tests, with and without real agents
99+- [scripts/agents.sh](../reference/agents-script.md)
100+- [How to publish a release](publish-a-release.md)
new file mode 100644
@@ -0,0 +1,100 @@
1+# How to run the end-to-end scenarios (v0.1)
2+
3+This guide runs the six scenarios that validate a release with the real test agents (issue #34), then the checks of what v0.1 added. It assumes a Mac with `mm`, llmman running, the models of the four test agents pulled, and Sidekicks built from the release's tag (`make package-macos`, or `make dev`).
4+
5+Two agents at most run at once (`limits.maxRunningAgents: 2`). Between two scenarios, unload every model, so each scenario starts cold:
6+
7+```bash
8+scripts/agents.sh unload --all
9+scripts/agents.sh ps # no model loaded
10+```
11+
12+## 0. Prepare
13+
14+1. `make install-test-config` (the four test agents at the default location; the previous file is kept as a backup).
15+2. Open Sidekicks. Under **AGENTS**: Alice, Bob, Riker, Garfield, all "stopped".
16+3. Open the [report](#report) below in a text editor, to fill it in as you go.
17+
18+## 1. Alice — a simple conversation, Markdown, cancelling
19+
20+1. Click **Alice (assistant)**, send: `Donne un tableau markdown de 2 lignes (nom, rôle) puis un bloc de code Go qui affiche hello.`
21+2. Expected: the answer streams; a table and coloured Go code with **Copy** appear.
22+3. Send: `Écris un poème de 40 vers sur la mer.` and click **■ Stop** after a few lines.
23+4. Expected: the answer stops and ends with "Answer stopped."; Alice goes back to "ready".
24+
25+## 2. Alice + Bob — a multi-agent channel, routing, shared context
26+
27+1. Create a channel `#e2e` (**⌘N**). Send: `@alice donne un prénom pour un chat roux.`
28+2. Expected: "Alice (assistant) joined the channel." then her answer with a name.
29+3. Send: `@bob que penses-tu du prénom proposé par Alice ?`
30+4. Expected: Bob joins and quotes the name Alice gave: he received the messages he had not seen.
31+5. Send a message without any mention: expected, the last agent who spoke (Bob) answers.
32+
33+## 3. Riker — tool calls, permissions
34+
35+1. Unload the models. Click **Riker (tools)**, send: `Crée le fichier e2e.txt contenant "ok", puis liste le dossier.`
36+2. Expected: a permission card in plain words. Click **Allow once**: the tool card turns ✓, `e2e.txt` is listed.
37+3. Send: `Supprime e2e.txt.` Click **Reject once**.
38+4. Expected: Riker says it could not; the file is still there (📂 → Files).
39+
40+## 4. Garfield — slash commands and skills
41+
42+1. Unload the models. Click **Garfield (translator)**, type `/`.
43+2. Expected: "Asking Garfield (translator) for their commands…" then Garfield's commands and skills (`idioms`, `styles`…).
44+3. Pick `/idioms` (Tab), add `il pleut des cordes`, send.
45+4. Expected: the answer explains the idiom with an English equivalent.
46+
47+## 5. Garfield + Bob — `/garfield:command` in a channel
48+
49+1. Unload the models. In `#e2e`, invite Garfield (**+ Invite**), then type `/gar`.
50+2. Expected: `/garfield:idioms`, `/garfield:styles`… listed with their agent.
51+3. Send `/garfield:styles Bonjour à tous`. Expected: only Garfield answers.
52+4. Send `@bob traduis la première version de Garfield en espagnol.` Expected: Bob answers from Garfield's text.
53+
54+## 6. Alice — restart and resume
55+
56+1. Quit Sidekicks (⌘Q) while nothing is answering. Reopen it.
57+2. Expected: `#e2e` and the direct conversations are there with their messages; the agents are "stopped".
58+3. In Alice's conversation, send: `Quelle était ma première question ?`
59+4. Expected: Alice answers from the earlier exchange (her session was resumed, or the history was given back to her).
60+
61+## 7. What v0.1 added
62+
63+| # | Check | Expected |
64+| --- | --- | --- |
65+| 7.1 | `mv ~/Library/Application\ Support/sidekicks ~/sidekicks-backup`, open Sidekicks | Welcome screen: logo, three steps, **+ Add my first agent** |
66+| 7.2 | Add Alice with the assistant (program `mm`, arguments `-acp -config agent.yaml`, folder `agents/alice`) | Her conversation opens; Settings shows her entry, the file's comments are kept |
67+| 7.3 | Add an agent whose program is `nothere`, start it (▶) | "The program "nothere" cannot be found…", with **Technical details** folded |
68+| 7.4 | Add an agent in a container that does not exist, start it | A sentence about the missing container |
69+| 7.5 | ⌘K, type `ali`, Enter; ⌘N; ⌘L; ⌘,; ⌘J; ⌘B; ⇧⌘N | Each does what [Keyboard shortcuts](../reference/keyboard-shortcuts.md) says |
70+| 7.6 | Tab through the window | The focused element is always outlined |
71+| 7.7 | Open the `.app` installed from the `.dmg` **from the Finder**, start Alice | It starts: `mm` is found without a full path (the shell's PATH) |
72+| 7.8 | Put the backup back: `rm -r ~/Library/Application\ Support/sidekicks && mv ~/sidekicks-backup ~/Library/Application\ Support/sidekicks` | Your agents and history are back |
73+
74+## Report
75+
76+Copy this into a comment of issue #34 and fill it in:
77+
78+```markdown
79+## End-to-end report — Sidekicks <version>, <date>
80+
81+Mac: <model, RAM>, macOS <version> · mm <version> · build: <dmg | make dev>
82+
83+| # | Scenario | Result | Notes |
84+| --- | --- | --- | --- |
85+| 1 | Alice: conversation, Markdown, cancel | ✅ / ❌ | |
86+| 2 | Alice + Bob: channel, routing, shared context | ✅ / ❌ | |
87+| 3 | Riker: tools, allow / reject | ✅ / ❌ | |
88+| 4 | Garfield: / commands and skills | ✅ / ❌ | |
89+| 5 | Garfield + Bob: /garfield:command in a channel | ✅ / ❌ | |
90+| 6 | Alice: restart and resume | ✅ / ❌ | |
91+| 7 | v0.1 checks (7.1–7.8) | ✅ / ❌ | |
92+
93+Models unloaded between scenarios: yes / no
94+```
95+
96+## See also
97+
98+- [How to run the tests](run-the-tests.md) — the automated tests, with and without real agents
99+- [scripts/agents.sh](../reference/agents-script.md)
100+- [How to publish a release](publish-a-release.md)
modified docs/en/how-to/run-the-tests.md +2 -2
@@ -22,8 +22,8 @@ The CI (`.github/workflows/ci.yml`) runs the same command, plus `make bindings-c
2222 | To check | Command |
2323 | --- | --- |
2424 | Go packages | `make test-go`, or `go test -race ./internal/agents -run Limit -v` for one test |
25-| Frontend (vitest) | `make test-frontend` |
26-| Shell tooling | `make test-scripts` |
25+| Frontend (vitest), including the WCAG contrast of the theme's colours | `make test-frontend` |
26+| Shell tooling (agents.sh, licence headers, package names, release tag) | `make test-scripts` |
2727 | Go lint (golangci-lint, pinned in `tools/go.mod`) | `make lint-go` |
2828 | Frontend types and Svelte warnings | `make lint-frontend` |
2929 | Licence headers | `make lint-headers` |
@@ -22,8 +22,8 @@ The CI (`.github/workflows/ci.yml`) runs the same command, plus `make bindings-c
22 | To check | Command |22 | To check | Command |
23 | --- | --- |23 | --- | --- |
24 | Go packages | `make test-go`, or `go test -race ./internal/agents -run Limit -v` for one test |24 | Go packages | `make test-go`, or `go test -race ./internal/agents -run Limit -v` for one test |
25-| Frontend (vitest) | `make test-frontend` |25+| Frontend (vitest), including the WCAG contrast of the theme's colours | `make test-frontend` |
26-| Shell tooling | `make test-scripts` |26+| Shell tooling (agents.sh, licence headers, package names, release tag) | `make test-scripts` |
27 | Go lint (golangci-lint, pinned in `tools/go.mod`) | `make lint-go` |27 | Go lint (golangci-lint, pinned in `tools/go.mod`) | `make lint-go` |
28 | Frontend types and Svelte warnings | `make lint-frontend` |28 | Frontend types and Svelte warnings | `make lint-frontend` |
29 | Licence headers | `make lint-headers` |29 | Licence headers | `make lint-headers` |
modified docs/en/reference/commands.md +47 -2
@@ -25,12 +25,17 @@ The order is detailed in the [configuration reference](configuration.md#location
2525 | `dev` | `wails3 dev`: the application with hot reload. |
2626 | `build` | `wails3 build`: `bin/sidekicks`. |
2727 | `package` | `wails3 package`: on macOS `bin/sidekicks.app`. |
28+| `package-linux` | `scripts/package.sh linux`: `.deb`, `.rpm`, `.AppImage` and `.tar.gz` for the machine's architecture, into `dist/`. Needs `file` (AppImage) and `rpm`. |
29+| `package-windows` | `scripts/package.sh windows`: NSIS installer and `.zip` for Windows amd64, cross-built, into `dist/`. Needs `makensis` and `zip`. |
30+| `release-build` | `scripts/build-release.sh`: on the Mac, every release package into `dist/` and `SHA256SUMS`. |
31+| `release-tag` | `scripts/tag-release.sh`: tags `v<version>` on `main` and pushes it. |
32+| `package-macos` | `scripts/package.sh macos`: universal `.dmg` and `.zip` (arm64 + amd64, signed ad hoc), into `dist/`. macOS only. |
2833 | `install-test-config` | `scripts/install-test-config.sh`: writes `examples/test-agents.yaml` at the default configuration location with absolute paths to the repository: each agent is `mm -acp -config agent.yaml` started in its folder, with `afterStop: [<repo>/scripts/agents.sh, unload, <agent>]` to unload its model when it stops, `mm` written with its absolute path (`MM_BIN`, or the one in `PATH`), and the current `PATH` in each agent's `env`; an existing file is kept as `config.yaml.<date>.bak`. |
2934 | `check` | `lint` then `test`. |
3035 | `test` | `test-go`, `test-frontend`, `test-scripts`. |
3136 | `test-go` | `go test ./...` (builds `frontend/dist` first if missing). |
3237 | `test-frontend` | `vitest run` in `frontend/`. |
33-| `test-scripts` | `scripts/agents_test.sh` and `scripts/check-license-headers_test.sh`. |
38+| `test-scripts` | `scripts/agents_test.sh`, `scripts/check-license-headers_test.sh`, `scripts/package_test.sh` and `scripts/tag-release_test.sh`. |
3439 | `test-real` | `go test -tags realagents -count=1 ./...` — needs `mm` and llmman. |
3540 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |
3641 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |
@@ -46,6 +51,46 @@ scripts/check-license-headers.sh [repository root]
4651
4752 Lists the source files (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) known to git whose first three lines do not contain `Sidekicks Source-Available License`, and exits 1 if there is one. Not checked: `frontend/bindings`, Wails' `build/` and `Taskfile.yml`, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.
4853
54+## `scripts/package.sh`
55+
56+```text
57+scripts/package.sh linux | windows | macos
58+scripts/package.sh names <system> <arch>
59+scripts/package.sh version
60+```
61+
62+| Command | Effect |
63+| --- | --- |
64+| `linux`, `windows`, `macos` | Builds the packages of that system into `dist/` (see the `package-*` targets), then lists `dist/`. |
65+| `names <system> <arch>` | Prints the file names a build produces, one per line; builds nothing. |
66+| `version` | Prints `info.version` of `build/config.yml`. |
67+
68+| System | Files (`<v>` = version) |
69+| --- | --- |
70+| `linux` | `sidekicks_<v>_linux_<arch>.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-<x86_64\|aarch64>.AppImage` |
71+| `windows` | `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` |
72+| `macos` | `Sidekicks-<v>-macos-universal.dmg`, `Sidekicks-<v>-macos-universal.zip` |
73+
74+## `scripts/build-release.sh`
75+
76+```text
77+[LINUX_ARCHES="amd64 arm64"] [SKIP="macos windows linux"] scripts/build-release.sh
78+```
79+
80+Empties `dist/`, then builds: macOS with `scripts/package.sh macos` (macOS only), Windows with `scripts/package.sh windows` (needs `makensis`), Linux with `scripts/package.sh linux` in a container per architecture of `LINUX_ARCHES` (default `amd64`), built from `scripts/linux-packages.Dockerfile` (`ubuntu:26.04`, Go of `go.mod`, pnpm of `frontend/package.json`, Wails CLI) with the repository mounted at `/src` and the Go module cache in the volume `sidekicks-gomod-<arch>`. Finally writes `dist/SHA256SUMS`. `SKIP` leaves the systems it names out. Fails when `makensis` is missing or Docker is not running, naming the system to skip.
81+
82+## `scripts/tag-release.sh`
83+
84+```text
85+scripts/tag-release.sh [--remote <name>] [--no-push]
86+```
87+
88+Creates the annotated tag `v<version>` (`info.version` of `build/config.yml`, message `Sidekicks <version>`) and pushes it to `--remote` (default `origin`). Refuses when tracked files have changes, when the current branch is not `main`, when `main` differs from `<remote>/main` after a fetch, or when the tag exists locally. Warns, without refusing, when `dist/` misses a package of the version or `SHA256SUMS`. `--no-push` tags locally only.
89+
4990 ## CI
5091
51-`.github/workflows/ci.yml`, on every push and merge request: installs Go (from `go.mod`), Node 22, pnpm (from `frontend/package.json`), the GTK 4 / WebKitGTK 6 build packages and the Wails CLI, then runs `make bindings-check` and `make check`.
92+`.github/workflows/ci.yml`, on every push and merge request, in an `ubuntu:26.04` container (GTK ≥ 4.10): installs git, a C compiler, the GTK 4 / WebKitGTK 6 build packages, Node and npm from apt, Go from go.dev (version of `go.mod`), pnpm (version of `frontend/package.json`) and the Wails CLI, then runs `make bindings-check` and `make check`.
93+
94+## Release workflow
95+
96+`.github/workflows/release.yml`, on the push of a tag `v*`, with `permissions: contents: write`: fails when the tag is not `v` + `info.version` of `build/config.yml`, then creates the release `<tag>` named `Sidekicks <tag>` (`softprops/action-gh-release@v2`, through Rickub's GitHub-compatible API behind `GITHUB_API_URL`), with notes giving the commit, the install guides and the table of the files to expect (from `scripts/package.sh names`). It attaches no file: the packages are attached by hand.
@@ -25,12 +25,17 @@ The order is detailed in the [configuration reference](configuration.md#location
25 | `dev` | `wails3 dev`: the application with hot reload. |25 | `dev` | `wails3 dev`: the application with hot reload. |
26 | `build` | `wails3 build`: `bin/sidekicks`. |26 | `build` | `wails3 build`: `bin/sidekicks`. |
27 | `package` | `wails3 package`: on macOS `bin/sidekicks.app`. |27 | `package` | `wails3 package`: on macOS `bin/sidekicks.app`. |
28+| `package-linux` | `scripts/package.sh linux`: `.deb`, `.rpm`, `.AppImage` and `.tar.gz` for the machine's architecture, into `dist/`. Needs `file` (AppImage) and `rpm`. |
29+| `package-windows` | `scripts/package.sh windows`: NSIS installer and `.zip` for Windows amd64, cross-built, into `dist/`. Needs `makensis` and `zip`. |
30+| `release-build` | `scripts/build-release.sh`: on the Mac, every release package into `dist/` and `SHA256SUMS`. |
31+| `release-tag` | `scripts/tag-release.sh`: tags `v<version>` on `main` and pushes it. |
32+| `package-macos` | `scripts/package.sh macos`: universal `.dmg` and `.zip` (arm64 + amd64, signed ad hoc), into `dist/`. macOS only. |
28 | `install-test-config` | `scripts/install-test-config.sh`: writes `examples/test-agents.yaml` at the default configuration location with absolute paths to the repository: each agent is `mm -acp -config agent.yaml` started in its folder, with `afterStop: [<repo>/scripts/agents.sh, unload, <agent>]` to unload its model when it stops, `mm` written with its absolute path (`MM_BIN`, or the one in `PATH`), and the current `PATH` in each agent's `env`; an existing file is kept as `config.yaml.<date>.bak`. |33 | `install-test-config` | `scripts/install-test-config.sh`: writes `examples/test-agents.yaml` at the default configuration location with absolute paths to the repository: each agent is `mm -acp -config agent.yaml` started in its folder, with `afterStop: [<repo>/scripts/agents.sh, unload, <agent>]` to unload its model when it stops, `mm` written with its absolute path (`MM_BIN`, or the one in `PATH`), and the current `PATH` in each agent's `env`; an existing file is kept as `config.yaml.<date>.bak`. |
29 | `check` | `lint` then `test`. |34 | `check` | `lint` then `test`. |
30 | `test` | `test-go`, `test-frontend`, `test-scripts`. |35 | `test` | `test-go`, `test-frontend`, `test-scripts`. |
31 | `test-go` | `go test ./...` (builds `frontend/dist` first if missing). |36 | `test-go` | `go test ./...` (builds `frontend/dist` first if missing). |
32 | `test-frontend` | `vitest run` in `frontend/`. |37 | `test-frontend` | `vitest run` in `frontend/`. |
33-| `test-scripts` | `scripts/agents_test.sh` and `scripts/check-license-headers_test.sh`. |38+| `test-scripts` | `scripts/agents_test.sh`, `scripts/check-license-headers_test.sh`, `scripts/package_test.sh` and `scripts/tag-release_test.sh`. |
34 | `test-real` | `go test -tags realagents -count=1 ./...` — needs `mm` and llmman. |39 | `test-real` | `go test -tags realagents -count=1 ./...` — needs `mm` and llmman. |
35 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |40 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |
36 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |41 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |
@@ -46,6 +51,46 @@ scripts/check-license-headers.sh [repository root]
46 51
47 Lists the source files (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) known to git whose first three lines do not contain `Sidekicks Source-Available License`, and exits 1 if there is one. Not checked: `frontend/bindings`, Wails' `build/` and `Taskfile.yml`, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.52 Lists the source files (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) known to git whose first three lines do not contain `Sidekicks Source-Available License`, and exits 1 if there is one. Not checked: `frontend/bindings`, Wails' `build/` and `Taskfile.yml`, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.
48 53
54+## `scripts/package.sh`
55+
56+```text
57+scripts/package.sh linux | windows | macos
58+scripts/package.sh names <system> <arch>
59+scripts/package.sh version
60+```
61+
62+| Command | Effect |
63+| --- | --- |
64+| `linux`, `windows`, `macos` | Builds the packages of that system into `dist/` (see the `package-*` targets), then lists `dist/`. |
65+| `names <system> <arch>` | Prints the file names a build produces, one per line; builds nothing. |
66+| `version` | Prints `info.version` of `build/config.yml`. |
67+
68+| System | Files (`<v>` = version) |
69+| --- | --- |
70+| `linux` | `sidekicks_<v>_linux_<arch>.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-<x86_64\|aarch64>.AppImage` |
71+| `windows` | `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` |
72+| `macos` | `Sidekicks-<v>-macos-universal.dmg`, `Sidekicks-<v>-macos-universal.zip` |
73+
74+## `scripts/build-release.sh`
75+
76+```text
77+[LINUX_ARCHES="amd64 arm64"] [SKIP="macos windows linux"] scripts/build-release.sh
78+```
79+
80+Empties `dist/`, then builds: macOS with `scripts/package.sh macos` (macOS only), Windows with `scripts/package.sh windows` (needs `makensis`), Linux with `scripts/package.sh linux` in a container per architecture of `LINUX_ARCHES` (default `amd64`), built from `scripts/linux-packages.Dockerfile` (`ubuntu:26.04`, Go of `go.mod`, pnpm of `frontend/package.json`, Wails CLI) with the repository mounted at `/src` and the Go module cache in the volume `sidekicks-gomod-<arch>`. Finally writes `dist/SHA256SUMS`. `SKIP` leaves the systems it names out. Fails when `makensis` is missing or Docker is not running, naming the system to skip.
81+
82+## `scripts/tag-release.sh`
83+
84+```text
85+scripts/tag-release.sh [--remote <name>] [--no-push]
86+```
87+
88+Creates the annotated tag `v<version>` (`info.version` of `build/config.yml`, message `Sidekicks <version>`) and pushes it to `--remote` (default `origin`). Refuses when tracked files have changes, when the current branch is not `main`, when `main` differs from `<remote>/main` after a fetch, or when the tag exists locally. Warns, without refusing, when `dist/` misses a package of the version or `SHA256SUMS`. `--no-push` tags locally only.
89+
49 ## CI90 ## CI
50 91
51-`.github/workflows/ci.yml`, on every push and merge request: installs Go (from `go.mod`), Node 22, pnpm (from `frontend/package.json`), the GTK 4 / WebKitGTK 6 build packages and the Wails CLI, then runs `make bindings-check` and `make check`.92+`.github/workflows/ci.yml`, on every push and merge request, in an `ubuntu:26.04` container (GTK ≥ 4.10): installs git, a C compiler, the GTK 4 / WebKitGTK 6 build packages, Node and npm from apt, Go from go.dev (version of `go.mod`), pnpm (version of `frontend/package.json`) and the Wails CLI, then runs `make bindings-check` and `make check`.
93+
94+## Release workflow
95+
96+`.github/workflows/release.yml`, on the push of a tag `v*`, with `permissions: contents: write`: fails when the tag is not `v` + `info.version` of `build/config.yml`, then creates the release `<tag>` named `Sidekicks <tag>` (`softprops/action-gh-release@v2`, through Rickub's GitHub-compatible API behind `GITHUB_API_URL`), with notes giving the commit, the install guides and the table of the files to expect (from `scripts/package.sh names`). It attaches no file: the packages are attached by hand.
modified docs/en/reference/configuration.md +14 -0
@@ -94,6 +94,20 @@ The runtime tells Sidekicks how to reach the agent's **files and terminal** (Age
9494
9595 A channel declared here is created at startup, or when the file is reloaded, **if no channel has this name yet** (whatever the case); after that it lives in the application like any other channel, and can be renamed or deleted there. Removing it from the file does not delete it (deleting history must be an explicit user action). Each configuration file has its own channels and history ([where it is kept](agents-and-events.md#history)).
9696
97+## What the "Add an agent" assistant writes
98+
99+The assistant appends one entry at the end of `agents`; the rest of the file, comments included, is kept. Nothing is written when the result would not be valid.
100+
101+| Field of the assistant | Written as |
102+| --- | --- |
103+| Name used with @, Name shown, Icon, What it does | `name`, `title`, `icon`, `description` (empty fields left out) |
104+| Agent's folder | `workspace`, as typed (`~` and relative paths are resolved like any `workspace`) |
105+| On this computer | `command: <program>`, `args: [<arguments>]` |
106+| In a Docker container | `command: docker`, `args: [exec, -i, <container>, <program>, <arguments>]`, `runtime: {type: container, container: <container>}` |
107+| In an sbx sandbox | `command: sbx`, `args: [exec, -i, <sandbox>, <program>, <arguments>]`, `runtime: {type: sbx, sandbox: <sandbox>}` |
108+
109+The arguments are split like a shell does for simple cases: spaces separate, single or double quotes group.
110+
97111 ## Rules checked by the loader, not by the schema
98112
99113 JSON Schema cannot express them; the configuration loader (issue #9) checks them and reports them like schema errors:
@@ -94,6 +94,20 @@ The runtime tells Sidekicks how to reach the agent's **files and terminal** (Age
94 94
95 A channel declared here is created at startup, or when the file is reloaded, **if no channel has this name yet** (whatever the case); after that it lives in the application like any other channel, and can be renamed or deleted there. Removing it from the file does not delete it (deleting history must be an explicit user action). Each configuration file has its own channels and history ([where it is kept](agents-and-events.md#history)).95 A channel declared here is created at startup, or when the file is reloaded, **if no channel has this name yet** (whatever the case); after that it lives in the application like any other channel, and can be renamed or deleted there. Removing it from the file does not delete it (deleting history must be an explicit user action). Each configuration file has its own channels and history ([where it is kept](agents-and-events.md#history)).
96 96
97+## What the "Add an agent" assistant writes
98+
99+The assistant appends one entry at the end of `agents`; the rest of the file, comments included, is kept. Nothing is written when the result would not be valid.
100+
101+| Field of the assistant | Written as |
102+| --- | --- |
103+| Name used with @, Name shown, Icon, What it does | `name`, `title`, `icon`, `description` (empty fields left out) |
104+| Agent's folder | `workspace`, as typed (`~` and relative paths are resolved like any `workspace`) |
105+| On this computer | `command: <program>`, `args: [<arguments>]` |
106+| In a Docker container | `command: docker`, `args: [exec, -i, <container>, <program>, <arguments>]`, `runtime: {type: container, container: <container>}` |
107+| In an sbx sandbox | `command: sbx`, `args: [exec, -i, <sandbox>, <program>, <arguments>]`, `runtime: {type: sbx, sandbox: <sandbox>}` |
108+
109+The arguments are split like a shell does for simple cases: spaces separate, single or double quotes group.
110+
97 ## Rules checked by the loader, not by the schema111 ## Rules checked by the loader, not by the schema
98 112
99 JSON Schema cannot express them; the configuration loader (issue #9) checks them and reports them like schema errors:113 JSON Schema cannot express them; the configuration loader (issue #9) checks them and reports them like schema errors:
added docs/en/reference/keyboard-shortcuts.md +51 -0
new file mode 100644
@@ -0,0 +1,51 @@
1+# Reference: keyboard shortcuts
2+
3+On macOS the shortcuts use **⌘**; on Linux and Windows, **Ctrl**. The table is `frontend/src/lib/shortcuts.ts`.
4+
5+## Application
6+
7+| macOS | Linux, Windows | Action |
8+| --- | --- | --- |
9+| ⌘K | Ctrl+K | Opens or closes the command palette |
10+| ⌘N | Ctrl+N | New channel (opens the channels dock when folded) |
11+| ⇧⌘N | Ctrl+Shift+N | Add an agent (the assistant) |
12+| ⌘L | Ctrl+L | Puts the cursor in the message box |
13+| ⌘B | Ctrl+B | Shows or hides the channels and agents dock |
14+| ⌘, | Ctrl+, | Shows or hides Settings |
15+| ⌘J | Ctrl+J | Shows or hides the log |
16+
17+On Linux and Windows, when the cursor is in an agent's terminal, the terminal keeps the Ctrl shortcuts (Ctrl+L clears the screen, Ctrl+K cuts the line…).
18+
19+## Command palette
20+
21+| Key | Action |
22+| --- | --- |
23+| letters | Filters: channels (`#` optional), agents' conversations, agents' files, actions |
24+| ↑ ↓ | Moves the selection |
25+| Enter | Runs the selection |
26+| Escape, or a click outside | Closes the palette |
27+
28+The palette lists, in order: the channels, a conversation per agent, the files of each agent, the actions of the table above, and **Open a configuration…**.
29+
30+## Message box
31+
32+| Key | Action |
33+| --- | --- |
34+| Enter | Sends |
35+| Shift+Enter | New line |
36+| ↑ (empty box) | Previous messages |
37+| `@`, `/` | Agent and command lists; Tab or Enter picks, ↑ ↓ move, Escape closes |
38+
39+## Editors (Settings, agents' files)
40+
41+| macOS | Linux, Windows | Action |
42+| --- | --- | --- |
43+| ⌘S | Ctrl+S | Saves |
44+
45+## Assistant, channels list
46+
47+| Key | Action |
48+| --- | --- |
49+| Enter | Validates the name of a new or renamed channel |
50+| Escape | Cancels it |
51+| Tab | Moves from field to field; every button is reachable, and the focused element is outlined |
new file mode 100644
@@ -0,0 +1,51 @@
1+# Reference: keyboard shortcuts
2+
3+On macOS the shortcuts use **⌘**; on Linux and Windows, **Ctrl**. The table is `frontend/src/lib/shortcuts.ts`.
4+
5+## Application
6+
7+| macOS | Linux, Windows | Action |
8+| --- | --- | --- |
9+| ⌘K | Ctrl+K | Opens or closes the command palette |
10+| ⌘N | Ctrl+N | New channel (opens the channels dock when folded) |
11+| ⇧⌘N | Ctrl+Shift+N | Add an agent (the assistant) |
12+| ⌘L | Ctrl+L | Puts the cursor in the message box |
13+| ⌘B | Ctrl+B | Shows or hides the channels and agents dock |
14+| ⌘, | Ctrl+, | Shows or hides Settings |
15+| ⌘J | Ctrl+J | Shows or hides the log |
16+
17+On Linux and Windows, when the cursor is in an agent's terminal, the terminal keeps the Ctrl shortcuts (Ctrl+L clears the screen, Ctrl+K cuts the line…).
18+
19+## Command palette
20+
21+| Key | Action |
22+| --- | --- |
23+| letters | Filters: channels (`#` optional), agents' conversations, agents' files, actions |
24+| ↑ ↓ | Moves the selection |
25+| Enter | Runs the selection |
26+| Escape, or a click outside | Closes the palette |
27+
28+The palette lists, in order: the channels, a conversation per agent, the files of each agent, the actions of the table above, and **Open a configuration…**.
29+
30+## Message box
31+
32+| Key | Action |
33+| --- | --- |
34+| Enter | Sends |
35+| Shift+Enter | New line |
36+| ↑ (empty box) | Previous messages |
37+| `@`, `/` | Agent and command lists; Tab or Enter picks, ↑ ↓ move, Escape closes |
38+
39+## Editors (Settings, agents' files)
40+
41+| macOS | Linux, Windows | Action |
42+| --- | --- | --- |
43+| ⌘S | Ctrl+S | Saves |
44+
45+## Assistant, channels list
46+
47+| Key | Action |
48+| --- | --- |
49+| Enter | Validates the name of a new or renamed channel |
50+| Escape | Cancels it |
51+| Tab | Moves from field to field; every button is reachable, and the focused element is outlined |
modified docs/en/tutorials/first-conversation.md +2 -0
@@ -25,6 +25,8 @@ You should see your message, then "Alice … writing…": the answer appears wor
2525
2626 Click **Copy**: it says "Copied ✓" — the code is in your clipboard.
2727
28+Now move the pointer over Alice's answer: **⧉ Copy** appears next to her name. Click it: the whole answer is in your clipboard, as Markdown (table and code included).
29+
2830 ## Step 3 — Let Riker run a command
2931
3032 Click **Riker (tools)** and send:
@@ -25,6 +25,8 @@ You should see your message, then "Alice … writing…": the answer appears wor
25 25
26 Click **Copy**: it says "Copied ✓" — the code is in your clipboard.26 Click **Copy**: it says "Copied ✓" — the code is in your clipboard.
27 27
28+Now move the pointer over Alice's answer: **⧉ Copy** appears next to her name. Click it: the whole answer is in your clipboard, as Markdown (table and code included).
29+
28 ## Step 3 — Let Riker run a command30 ## Step 3 — Let Riker run a command
29 31
30 Click **Riker (tools)** and send:32 Click **Riker (tools)** and send:
added docs/en/tutorials/your-first-agent.md +64 -0
new file mode 100644
@@ -0,0 +1,64 @@
1+# Tutorial: your first agent, without writing any configuration
2+
3+By the end of this tutorial, you will have added an agent to Sidekicks with the "Add an agent" assistant, talked with it, and found it again with the command palette. You will not open the configuration file once.
4+
5+## Prerequisites
6+
7+- Sidekicks installed: see [How to install Sidekicks](../how-to/install-sidekicks.md).
8+- `mm` installed, and llmman running with the model of Alice pulled (see [Your first ACP exchange with a test agent](first-acp-exchange.md)).
9+- The Sidekicks repository on your computer: Alice's folder, `agents/alice`, holds her `agent.yaml`. Below, it is `~/sidekicks/agents/alice`; use your own path.
10+
11+## Step 1 — Open Sidekicks
12+
13+Open Sidekicks from your Applications folder (or your applications menu).
14+
15+You should see the Sidekicks logo, "Welcome to Sidekicks", three numbered steps and a blue **+ Add my first agent** button. If agents are already listed on the left, use **+ Add an agent** under them instead: the rest is the same.
16+
17+## Step 2 — Say who the agent is
18+
19+Click **+ Add my first agent**. In **Name shown**, type:
20+
21+```text
22+Alice (assistant)
23+```
24+
25+You should see **Name used with @** fill itself in with `alice-assistant`. Replace it with `alice`: that is the name you will use to call her.
26+
27+## Step 3 — Say how to start it
28+
29+Leave **On this computer** selected. Fill in:
30+
31+| Field | Value |
32+| --- | --- |
33+| Program | `mm` |
34+| Arguments | `-acp -config agent.yaml` |
35+| Agent's folder | `~/sidekicks/agents/alice` (or **Choose…** and pick the folder) |
36+
37+Click **Add the agent**.
38+
39+You should see the assistant close, **🤖 Alice (assistant)** appear under **AGENTS** and under **DIRECT MESSAGES**, and her conversation open in the centre with "Write your first message to Alice (assistant)."
40+
41+## Step 4 — Talk with Alice
42+
43+Click the text box at the bottom, type the following and press Enter:
44+
45+```text
46+Bonjour Alice, présente-toi en une phrase.
47+```
48+
49+You should see Alice's dot turn orange while she starts and answers, then her sentence appear word by word.
50+
51+## Step 5 — Find her again with the keyboard
52+
53+Click **# general** on the left. Then press **⌘K** (Ctrl+K on Linux and Windows) and type `ali`.
54+
55+You should see a list with **Alice (assistant)** first. Press Enter: her conversation is back in the centre.
56+
57+## What now?
58+
59+You have added an agent without touching any YAML, talked with it and moved around with the keyboard. To go further:
60+
61+- To add an agent that runs in Docker or in an sbx sandbox → [How to add an agent](../how-to/add-an-agent.md)
62+- To bring several agents into one channel → [A channel with two agents](first-channel.md)
63+- Every shortcut → [Keyboard shortcuts](../reference/keyboard-shortcuts.md)
64+- What the assistant wrote → [The configuration file](../reference/configuration.md)
new file mode 100644
@@ -0,0 +1,64 @@
1+# Tutorial: your first agent, without writing any configuration
2+
3+By the end of this tutorial, you will have added an agent to Sidekicks with the "Add an agent" assistant, talked with it, and found it again with the command palette. You will not open the configuration file once.
4+
5+## Prerequisites
6+
7+- Sidekicks installed: see [How to install Sidekicks](../how-to/install-sidekicks.md).
8+- `mm` installed, and llmman running with the model of Alice pulled (see [Your first ACP exchange with a test agent](first-acp-exchange.md)).
9+- The Sidekicks repository on your computer: Alice's folder, `agents/alice`, holds her `agent.yaml`. Below, it is `~/sidekicks/agents/alice`; use your own path.
10+
11+## Step 1 — Open Sidekicks
12+
13+Open Sidekicks from your Applications folder (or your applications menu).
14+
15+You should see the Sidekicks logo, "Welcome to Sidekicks", three numbered steps and a blue **+ Add my first agent** button. If agents are already listed on the left, use **+ Add an agent** under them instead: the rest is the same.
16+
17+## Step 2 — Say who the agent is
18+
19+Click **+ Add my first agent**. In **Name shown**, type:
20+
21+```text
22+Alice (assistant)
23+```
24+
25+You should see **Name used with @** fill itself in with `alice-assistant`. Replace it with `alice`: that is the name you will use to call her.
26+
27+## Step 3 — Say how to start it
28+
29+Leave **On this computer** selected. Fill in:
30+
31+| Field | Value |
32+| --- | --- |
33+| Program | `mm` |
34+| Arguments | `-acp -config agent.yaml` |
35+| Agent's folder | `~/sidekicks/agents/alice` (or **Choose…** and pick the folder) |
36+
37+Click **Add the agent**.
38+
39+You should see the assistant close, **🤖 Alice (assistant)** appear under **AGENTS** and under **DIRECT MESSAGES**, and her conversation open in the centre with "Write your first message to Alice (assistant)."
40+
41+## Step 4 — Talk with Alice
42+
43+Click the text box at the bottom, type the following and press Enter:
44+
45+```text
46+Bonjour Alice, présente-toi en une phrase.
47+```
48+
49+You should see Alice's dot turn orange while she starts and answers, then her sentence appear word by word.
50+
51+## Step 5 — Find her again with the keyboard
52+
53+Click **# general** on the left. Then press **⌘K** (Ctrl+K on Linux and Windows) and type `ali`.
54+
55+You should see a list with **Alice (assistant)** first. Press Enter: her conversation is back in the centre.
56+
57+## What now?
58+
59+You have added an agent without touching any YAML, talked with it and moved around with the keyboard. To go further:
60+
61+- To add an agent that runs in Docker or in an sbx sandbox → [How to add an agent](../how-to/add-an-agent.md)
62+- To bring several agents into one channel → [A channel with two agents](first-channel.md)
63+- Every shortcut → [Keyboard shortcuts](../reference/keyboard-shortcuts.md)
64+- What the assistant wrote → [The configuration file](../reference/configuration.md)
modified docs/fr/README.md +5 -5
@@ -1,14 +1,14 @@
11 # Documentation de Sidekicks (français)
22
3-Sidekicks est une application de bureau pour converser avec des agents d'IA dans des channels, grâce à l'Agent Client Protocol, pensée pour des personnes qui ne sont pas développeuses. La milestone M4 (Workspace des agents) est terminée : en plus des channels à plusieurs agents (`@`, `/`, historique gardé d'un lancement à l'autre), on parcourt et on modifie le dossier de chaque agent, et on y ouvre un terminal — sur cet ordinateur ou dans un conteneur Docker. Les réponses sont mises en forme, les outils demandent votre accord, et la configuration se modifie depuis la fenêtre.
3+Sidekicks est une application de bureau pour converser avec des agents d'IA dans des channels, grâce à l'Agent Client Protocol, pensée pour des personnes qui ne sont pas développeuses. La version 0.1 est la première release : des paquets pour macOS, Linux et Windows, un assistant qui ajoute des agents sans écrire de YAML, des messages d'erreur en phrases claires, des raccourcis clavier et une palette de commandes — en plus des channels à plusieurs agents (`@`, `/`, historique gardé d'un lancement à l'autre), du dossier de chaque agent avec un éditeur et un terminal (sur cet ordinateur, dans Docker ou dans une sandbox sbx), des réponses mises en forme et des outils qui demandent votre accord.
44
55 Cette documentation suit la méthode [Diátaxis](https://diataxis.fr) : choisissez la section qui correspond à votre besoin du moment.
66
77 | Vous voulez… | Section | Pages |
88 | --- | --- | --- |
9-| **apprendre** en pratiquant | Tutoriels | [Un channel avec deux agents](tutorials/premier-channel.md) · [Votre première conversation avec un agent](tutorials/premiere-conversation.md) · [Votre premier lancement de Sidekicks](tutorials/premier-lancement.md) · [Votre premier échange ACP avec un agent de test](tutorials/premier-echange-acp.md) |
10-| **réaliser** une tâche précise | Guides pratiques | [Travailler avec les fichiers et le terminal d'un agent](how-to/fichiers-et-terminal-d-un-agent.md) · [Lancer un agent dans un conteneur Docker](how-to/lancer-un-agent-dans-docker.md) · [Lancer un agent dans une sandbox sbx](how-to/lancer-un-agent-dans-une-sandbox.md) · [Gérer les channels et leurs agents](how-to/gerer-les-channels.md) · [Modifier ou changer la configuration](how-to/modifier-ou-changer-la-configuration.md) · [Repartir de zéro](how-to/repartir-de-zero.md) · [Développer l'application](how-to/developper-l-application.md) · [Lancer les tests](how-to/lancer-les-tests.md) · [Lancer un agent de test](how-to/lancer-un-agent-de-test.md) · [Tester la milestone M4 sous macOS](how-to/tester-la-milestone-m4-sous-macos.md) · [Tester la milestone M3 sous macOS](how-to/tester-la-milestone-m3-sous-macos.md) · [Tester la milestone M2 sous macOS](how-to/tester-la-milestone-m2-sous-macos.md) · [Tester la milestone M1 sous macOS](how-to/tester-la-milestone-m1-sous-macos.md) · [Tester la milestone Initialize sous macOS](how-to/tester-la-milestone-initialize-sous-macos.md) |
11-| **consulter** un détail exact | Référence | [Le fichier de configuration](reference/configuration.md) · [Agents, channels et événements](reference/agents-et-evenements.md) · [Commandes](reference/commandes.md) · [scripts/agents.sh](reference/script-agents.md) |
9+| **apprendre** en pratiquant | Tutoriels | [Votre premier agent, sans écrire de configuration](tutorials/premier-agent.md) · [Un channel avec deux agents](tutorials/premier-channel.md) · [Votre première conversation avec un agent](tutorials/premiere-conversation.md) · [Votre premier lancement de Sidekicks](tutorials/premier-lancement.md) · [Votre premier échange ACP avec un agent de test](tutorials/premier-echange-acp.md) |
10+| **réaliser** une tâche précise | Guides pratiques | [Installer Sidekicks](how-to/installer-sidekicks.md) · [Ajouter un agent](how-to/ajouter-un-agent.md) · [Travailler avec les fichiers et le terminal d'un agent](how-to/fichiers-et-terminal-d-un-agent.md) · [Lancer un agent dans un conteneur Docker](how-to/lancer-un-agent-dans-docker.md) · [Lancer un agent dans une sandbox sbx](how-to/lancer-un-agent-dans-une-sandbox.md) · [Gérer les channels et leurs agents](how-to/gerer-les-channels.md) · [Modifier ou changer la configuration](how-to/modifier-ou-changer-la-configuration.md) · [Repartir de zéro](how-to/repartir-de-zero.md) · [Développer l'application](how-to/developper-l-application.md) · [Lancer les tests](how-to/lancer-les-tests.md) · [Lancer un agent de test](how-to/lancer-un-agent-de-test.md) · [Dérouler les scénarios de bout en bout (v0.1)](how-to/derouler-les-scenarios-de-bout-en-bout.md) · [Publier une release](how-to/publier-une-release.md) · [Tester la milestone M4 sous macOS](how-to/tester-la-milestone-m4-sous-macos.md) · [Tester la milestone M3 sous macOS](how-to/tester-la-milestone-m3-sous-macos.md) · [Tester la milestone M2 sous macOS](how-to/tester-la-milestone-m2-sous-macos.md) · [Tester la milestone M1 sous macOS](how-to/tester-la-milestone-m1-sous-macos.md) · [Tester la milestone Initialize sous macOS](how-to/tester-la-milestone-initialize-sous-macos.md) |
11+| **consulter** un détail exact | Référence | [Le fichier de configuration](reference/configuration.md) · [Raccourcis clavier](reference/raccourcis-clavier.md) · [Agents, channels et événements](reference/agents-et-evenements.md) · [Commandes](reference/commandes.md) · [scripts/agents.sh](reference/script-agents.md) |
1212 | **comprendre** le pourquoi | Explications | [Workspaces et terminaux](explanation/workspaces-et-terminaux.md) · [Les channels et ce que sait chaque agent](explanation/channels-et-contexte.md) · [Permissions et confiance](explanation/permissions-et-confiance.md) · [Pourquoi ACP](explanation/pourquoi-acp.md) · [La stack de l'interface](explanation/stack-interface.md) · [Architecture](explanation/architecture.md) · [Mémoire et modèles locaux](explanation/memoire-et-modeles-locaux.md) |
1313
1414 ## Documents de conception
@@ -16,6 +16,6 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr) : choisiss
1616 Communs aux deux langues et rédigés en anglais :
1717
1818 - [Décisions d'architecture (ADR)](../adr/) — ACP, stack de l'interface, architecture, conversations, langue de l'interface, channels, workspaces des agents
19-- [Maquettes](../design/README.md) — les six écrans principaux (textes d'interface en français)
19+- [Maquettes](../design/README.md) — les six écrans principaux (textes d'interface en anglais, la langue par défaut)
2020 - [Schéma des packages](../diagrams/packages.drawio)
2121 - [Traces ACP](../spikes/acp/README.md) enregistrées avec les vrais agents
@@ -1,14 +1,14 @@
1 # Documentation de Sidekicks (français)1 # Documentation de Sidekicks (français)
2 2
3-Sidekicks est une application de bureau pour converser avec des agents d'IA dans des channels, grâce à l'Agent Client Protocol, pensée pour des personnes qui ne sont pas développeuses. La milestone M4 (Workspace des agents) est terminée : en plus des channels à plusieurs agents (`@`, `/`, historique gardé d'un lancement à l'autre), on parcourt et on modifie le dossier de chaque agent, et on y ouvre un terminal — sur cet ordinateur ou dans un conteneur Docker. Les réponses sont mises en forme, les outils demandent votre accord, et la configuration se modifie depuis la fenêtre.3+Sidekicks est une application de bureau pour converser avec des agents d'IA dans des channels, grâce à l'Agent Client Protocol, pensée pour des personnes qui ne sont pas développeuses. La version 0.1 est la première release : des paquets pour macOS, Linux et Windows, un assistant qui ajoute des agents sans écrire de YAML, des messages d'erreur en phrases claires, des raccourcis clavier et une palette de commandes — en plus des channels à plusieurs agents (`@`, `/`, historique gardé d'un lancement à l'autre), du dossier de chaque agent avec un éditeur et un terminal (sur cet ordinateur, dans Docker ou dans une sandbox sbx), des réponses mises en forme et des outils qui demandent votre accord.
4 4
5 Cette documentation suit la méthode [Diátaxis](https://diataxis.fr) : choisissez la section qui correspond à votre besoin du moment.5 Cette documentation suit la méthode [Diátaxis](https://diataxis.fr) : choisissez la section qui correspond à votre besoin du moment.
6 6
7 | Vous voulez… | Section | Pages |7 | Vous voulez… | Section | Pages |
8 | --- | --- | --- |8 | --- | --- | --- |
9-| **apprendre** en pratiquant | Tutoriels | [Un channel avec deux agents](tutorials/premier-channel.md) · [Votre première conversation avec un agent](tutorials/premiere-conversation.md) · [Votre premier lancement de Sidekicks](tutorials/premier-lancement.md) · [Votre premier échange ACP avec un agent de test](tutorials/premier-echange-acp.md) |9+| **apprendre** en pratiquant | Tutoriels | [Votre premier agent, sans écrire de configuration](tutorials/premier-agent.md) · [Un channel avec deux agents](tutorials/premier-channel.md) · [Votre première conversation avec un agent](tutorials/premiere-conversation.md) · [Votre premier lancement de Sidekicks](tutorials/premier-lancement.md) · [Votre premier échange ACP avec un agent de test](tutorials/premier-echange-acp.md) |
10-| **réaliser** une tâche précise | Guides pratiques | [Travailler avec les fichiers et le terminal d'un agent](how-to/fichiers-et-terminal-d-un-agent.md) · [Lancer un agent dans un conteneur Docker](how-to/lancer-un-agent-dans-docker.md) · [Lancer un agent dans une sandbox sbx](how-to/lancer-un-agent-dans-une-sandbox.md) · [Gérer les channels et leurs agents](how-to/gerer-les-channels.md) · [Modifier ou changer la configuration](how-to/modifier-ou-changer-la-configuration.md) · [Repartir de zéro](how-to/repartir-de-zero.md) · [Développer l'application](how-to/developper-l-application.md) · [Lancer les tests](how-to/lancer-les-tests.md) · [Lancer un agent de test](how-to/lancer-un-agent-de-test.md) · [Tester la milestone M4 sous macOS](how-to/tester-la-milestone-m4-sous-macos.md) · [Tester la milestone M3 sous macOS](how-to/tester-la-milestone-m3-sous-macos.md) · [Tester la milestone M2 sous macOS](how-to/tester-la-milestone-m2-sous-macos.md) · [Tester la milestone M1 sous macOS](how-to/tester-la-milestone-m1-sous-macos.md) · [Tester la milestone Initialize sous macOS](how-to/tester-la-milestone-initialize-sous-macos.md) |10+| **réaliser** une tâche précise | Guides pratiques | [Installer Sidekicks](how-to/installer-sidekicks.md) · [Ajouter un agent](how-to/ajouter-un-agent.md) · [Travailler avec les fichiers et le terminal d'un agent](how-to/fichiers-et-terminal-d-un-agent.md) · [Lancer un agent dans un conteneur Docker](how-to/lancer-un-agent-dans-docker.md) · [Lancer un agent dans une sandbox sbx](how-to/lancer-un-agent-dans-une-sandbox.md) · [Gérer les channels et leurs agents](how-to/gerer-les-channels.md) · [Modifier ou changer la configuration](how-to/modifier-ou-changer-la-configuration.md) · [Repartir de zéro](how-to/repartir-de-zero.md) · [Développer l'application](how-to/developper-l-application.md) · [Lancer les tests](how-to/lancer-les-tests.md) · [Lancer un agent de test](how-to/lancer-un-agent-de-test.md) · [Dérouler les scénarios de bout en bout (v0.1)](how-to/derouler-les-scenarios-de-bout-en-bout.md) · [Publier une release](how-to/publier-une-release.md) · [Tester la milestone M4 sous macOS](how-to/tester-la-milestone-m4-sous-macos.md) · [Tester la milestone M3 sous macOS](how-to/tester-la-milestone-m3-sous-macos.md) · [Tester la milestone M2 sous macOS](how-to/tester-la-milestone-m2-sous-macos.md) · [Tester la milestone M1 sous macOS](how-to/tester-la-milestone-m1-sous-macos.md) · [Tester la milestone Initialize sous macOS](how-to/tester-la-milestone-initialize-sous-macos.md) |
11-| **consulter** un détail exact | Référence | [Le fichier de configuration](reference/configuration.md) · [Agents, channels et événements](reference/agents-et-evenements.md) · [Commandes](reference/commandes.md) · [scripts/agents.sh](reference/script-agents.md) |11+| **consulter** un détail exact | Référence | [Le fichier de configuration](reference/configuration.md) · [Raccourcis clavier](reference/raccourcis-clavier.md) · [Agents, channels et événements](reference/agents-et-evenements.md) · [Commandes](reference/commandes.md) · [scripts/agents.sh](reference/script-agents.md) |
12 | **comprendre** le pourquoi | Explications | [Workspaces et terminaux](explanation/workspaces-et-terminaux.md) · [Les channels et ce que sait chaque agent](explanation/channels-et-contexte.md) · [Permissions et confiance](explanation/permissions-et-confiance.md) · [Pourquoi ACP](explanation/pourquoi-acp.md) · [La stack de l'interface](explanation/stack-interface.md) · [Architecture](explanation/architecture.md) · [Mémoire et modèles locaux](explanation/memoire-et-modeles-locaux.md) |12 | **comprendre** le pourquoi | Explications | [Workspaces et terminaux](explanation/workspaces-et-terminaux.md) · [Les channels et ce que sait chaque agent](explanation/channels-et-contexte.md) · [Permissions et confiance](explanation/permissions-et-confiance.md) · [Pourquoi ACP](explanation/pourquoi-acp.md) · [La stack de l'interface](explanation/stack-interface.md) · [Architecture](explanation/architecture.md) · [Mémoire et modèles locaux](explanation/memoire-et-modeles-locaux.md) |
13 13
14 ## Documents de conception14 ## Documents de conception
@@ -16,6 +16,6 @@ Cette documentation suit la méthode [Diátaxis](https://diataxis.fr) : choisiss
16 Communs aux deux langues et rédigés en anglais :16 Communs aux deux langues et rédigés en anglais :
17 17
18 - [Décisions d'architecture (ADR)](../adr/) — ACP, stack de l'interface, architecture, conversations, langue de l'interface, channels, workspaces des agents18 - [Décisions d'architecture (ADR)](../adr/) — ACP, stack de l'interface, architecture, conversations, langue de l'interface, channels, workspaces des agents
19-- [Maquettes](../design/README.md) — les six écrans principaux (textes d'interface en français)19+- [Maquettes](../design/README.md) — les six écrans principaux (textes d'interface en anglais, la langue par défaut)
20 - [Schéma des packages](../diagrams/packages.drawio)20 - [Schéma des packages](../diagrams/packages.drawio)
21 - [Traces ACP](../spikes/acp/README.md) enregistrées avec les vrais agents21 - [Traces ACP](../spikes/acp/README.md) enregistrées avec les vrais agents
modified docs/fr/explanation/architecture.md +9 -1
@@ -6,7 +6,7 @@ Avant d'écrire l'application, le projet a fixé le découpage du code, ce qui e
66
77 ![Packages et dépendances](../../diagrams/packages.png)
88
9-La source du schéma est [packages.drawio](../../diagrams/packages.drawio). Les boîtes bleues existent depuis la milestone M1 ; les grises sont prévues.
9+La source du schéma est [packages.drawio](../../diagrams/packages.drawio). Toutes les boîtes existent (v0.1).
1010
1111 ## Pourquoi ce découpage
1212
@@ -30,6 +30,14 @@ Chaque fichier de configuration a sa propre base : passer de « mes agents de tr
3030
3131 Pendant qu'un agent répond, la réponse vit en mémoire et l'interface reçoit chaque fragment. Elle est écrite sur disque à la fin du tour. Écrire cinquante fois par seconde userait le disque pour rien ; le prix est de perdre une réponse inachevée si l'application plante, ce qui est acceptable.
3232
33+## Les programmes sont trouvés comme dans un terminal
34+
35+Une application ouverte depuis le Finder n'hérite pas du `PATH` du terminal de l'utilisateur : macOS lui donne `/usr/bin:/bin:/usr/sbin:/sbin`, si bien que `mm`, `docker` ou `sbx` installés avec Homebrew ou dans `~/.local/bin` sont « introuvables », alors que l'utilisateur vient de les lancer dans un terminal. Demander à une personne qui n'est pas développeuse d'écrire des chemins complets serait la mauvaise réponse. Au démarrage, avant de lire la configuration, `main.go` demande son `PATH` au shell de l'utilisateur (`internal/shellpath` : un shell de connexion interactif, pour qu'il lise les mêmes fichiers qu'un terminal, avec une limite de cinq secondes) et le place devant le sien. Les agents en héritent, et les avertissements de la configuration (« programme introuvable ») sont calculés avec lui. Quand le shell ne peut pas être lu, rien ne change et le chemin complet reste la solution.
36+
37+## Des erreurs en phrases claires
38+
39+Go, les agents, Docker et sbx signalent leurs erreurs pour des développeurs (`exec: "mm": executable file not found in $PATH`). Le côté Go les garde telles quelles : elles sont précises, les tests les vérifient, et c'est ce qu'il faut à un développeur dans le journal. L'interface traduit les plus courantes (`frontend/src/lib/errors.ts`) en une phrase qui dit ce qui ne va pas et quoi faire, dans la langue de l'interface, et garde le texte d'origine replié sous **Détails techniques**. Une erreur inconnue est affichée telle quelle plutôt que cachée derrière une phrase vague.
40+
3341 ## Alternatives écartées
3442
3543 - **Stocker les conversations dans le fichier YAML** : cela transformerait un fichier que les gens modifient en base de données, en conflit avec les écritures de l'application.
@@ -6,7 +6,7 @@ Avant d'écrire l'application, le projet a fixé le découpage du code, ce qui e
6 6
7 ![Packages et dépendances](../../diagrams/packages.png)7 ![Packages et dépendances](../../diagrams/packages.png)
8 8
9-La source du schéma est [packages.drawio](../../diagrams/packages.drawio). Les boîtes bleues existent depuis la milestone M1 ; les grises sont prévues.9+La source du schéma est [packages.drawio](../../diagrams/packages.drawio). Toutes les boîtes existent (v0.1).
10 10
11 ## Pourquoi ce découpage11 ## Pourquoi ce découpage
12 12
@@ -30,6 +30,14 @@ Chaque fichier de configuration a sa propre base : passer de « mes agents de tr
30 30
31 Pendant qu'un agent répond, la réponse vit en mémoire et l'interface reçoit chaque fragment. Elle est écrite sur disque à la fin du tour. Écrire cinquante fois par seconde userait le disque pour rien ; le prix est de perdre une réponse inachevée si l'application plante, ce qui est acceptable.31 Pendant qu'un agent répond, la réponse vit en mémoire et l'interface reçoit chaque fragment. Elle est écrite sur disque à la fin du tour. Écrire cinquante fois par seconde userait le disque pour rien ; le prix est de perdre une réponse inachevée si l'application plante, ce qui est acceptable.
32 32
33+## Les programmes sont trouvés comme dans un terminal
34+
35+Une application ouverte depuis le Finder n'hérite pas du `PATH` du terminal de l'utilisateur : macOS lui donne `/usr/bin:/bin:/usr/sbin:/sbin`, si bien que `mm`, `docker` ou `sbx` installés avec Homebrew ou dans `~/.local/bin` sont « introuvables », alors que l'utilisateur vient de les lancer dans un terminal. Demander à une personne qui n'est pas développeuse d'écrire des chemins complets serait la mauvaise réponse. Au démarrage, avant de lire la configuration, `main.go` demande son `PATH` au shell de l'utilisateur (`internal/shellpath` : un shell de connexion interactif, pour qu'il lise les mêmes fichiers qu'un terminal, avec une limite de cinq secondes) et le place devant le sien. Les agents en héritent, et les avertissements de la configuration (« programme introuvable ») sont calculés avec lui. Quand le shell ne peut pas être lu, rien ne change et le chemin complet reste la solution.
36+
37+## Des erreurs en phrases claires
38+
39+Go, les agents, Docker et sbx signalent leurs erreurs pour des développeurs (`exec: "mm": executable file not found in $PATH`). Le côté Go les garde telles quelles : elles sont précises, les tests les vérifient, et c'est ce qu'il faut à un développeur dans le journal. L'interface traduit les plus courantes (`frontend/src/lib/errors.ts`) en une phrase qui dit ce qui ne va pas et quoi faire, dans la langue de l'interface, et garde le texte d'origine replié sous **Détails techniques**. Une erreur inconnue est affichée telle quelle plutôt que cachée derrière une phrase vague.
40+
33 ## Alternatives écartées41 ## Alternatives écartées
34 42
35 - **Stocker les conversations dans le fichier YAML** : cela transformerait un fichier que les gens modifient en base de données, en conflit avec les écritures de l'application.43 - **Stocker les conversations dans le fichier YAML** : cela transformerait un fichier que les gens modifient en base de données, en conflit avec les écritures de l'application.
added docs/fr/how-to/ajouter-un-agent.md +26 -0
new file mode 100644
@@ -0,0 +1,26 @@
1+# Ajouter un agent
2+
3+Ce guide montre comment ajouter un agent depuis la fenêtre de Sidekicks, avec l'assistant « Ajouter un agent », quel que soit l'endroit où il tourne : sur cet ordinateur, dans un conteneur Docker ou dans une sandbox sbx. Il suppose que le programme de l'agent parle ACP (pour `mm` : l'argument `-acp`).
4+
5+## Étapes
6+
7+1. Ouvrez l'assistant : **+ Ajouter mon premier agent** sur l'écran d'accueil, **+ Ajouter un agent** sous la liste des agents, ou **⇧⌘N** (Ctrl+Maj+N sous Linux et Windows).
8+2. **Qui est-ce ?** Remplissez **Nom affiché** (par exemple `Garfield (traducteur)`). **Nom utilisé avec @** le suit ; changez-le si vous en voulez un plus court (minuscules, chiffres, `-` et `_`, en commençant par une lettre). L'icône et la description sont facultatives.
9+3. **Où tourne-t-il ?** Choisissez l'environnement, puis remplissez ce qu'il demande (voir plus bas).
10+4. **Comment le démarrer ?** Remplissez **Programme**, **Arguments** (sur une ligne, comme dans un terminal ; les guillemets regroupent des mots) et **Dossier de l'agent**. Le dossier est créé s'il n'existe pas.
11+5. Cliquez sur **Ajouter l'agent**. Sa conversation directe s'ouvre dès que la configuration est rechargée.
12+
13+Quand l'agent ne peut pas être ajouté (un nom déjà pris, par exemple), l'assistant dit pourquoi et n'écrit rien.
14+
15+## Variantes
16+
17+- **Sur cet ordinateur** : le programme tourne ici, dans le dossier de l'agent. Si Sidekicks, ouvert depuis le Finder, ne trouve pas le programme, indiquez son chemin complet (`which mm` dans un terminal le donne). Sidekicks lit le `PATH` de votre shell au démarrage : c'est rarement nécessaire.
18+- **Dans un conteneur Docker** : indiquez le **Nom du conteneur**. Le conteneur doit tourner, avec le dossier de l'agent monté au même chemin, et contenir le programme : voir [Lancer un agent dans un conteneur Docker](lancer-un-agent-dans-docker.md). Sidekicks démarre l'agent avec `docker exec -i <conteneur> <programme> <arguments>`.
19+- **Dans une sandbox sbx** : indiquez le **Nom de la sandbox**. Elle doit exister, avec le dossier de l'agent au même chemin : voir [Lancer un agent dans une sandbox sbx](lancer-un-agent-dans-une-sandbox.md). Sidekicks démarre l'agent avec `sbx exec -i <sandbox> <programme> <arguments>`.
20+- **Les options que l'assistant ne demande pas** (`env`, `afterStop`, un autre `workspacePath`) : ajoutez l'agent avec l'assistant, puis complétez son entrée dans **Settings**.
21+
22+## Voir aussi
23+
24+- Ce que l'assistant écrit : [Le fichier de configuration](../reference/configuration.md)
25+- [Modifier ou changer la configuration](modifier-ou-changer-la-configuration.md)
26+- [Tutoriel : votre premier agent, sans écrire de configuration](../tutorials/premier-agent.md)
new file mode 100644
@@ -0,0 +1,26 @@
1+# Ajouter un agent
2+
3+Ce guide montre comment ajouter un agent depuis la fenêtre de Sidekicks, avec l'assistant « Ajouter un agent », quel que soit l'endroit où il tourne : sur cet ordinateur, dans un conteneur Docker ou dans une sandbox sbx. Il suppose que le programme de l'agent parle ACP (pour `mm` : l'argument `-acp`).
4+
5+## Étapes
6+
7+1. Ouvrez l'assistant : **+ Ajouter mon premier agent** sur l'écran d'accueil, **+ Ajouter un agent** sous la liste des agents, ou **⇧⌘N** (Ctrl+Maj+N sous Linux et Windows).
8+2. **Qui est-ce ?** Remplissez **Nom affiché** (par exemple `Garfield (traducteur)`). **Nom utilisé avec @** le suit ; changez-le si vous en voulez un plus court (minuscules, chiffres, `-` et `_`, en commençant par une lettre). L'icône et la description sont facultatives.
9+3. **Où tourne-t-il ?** Choisissez l'environnement, puis remplissez ce qu'il demande (voir plus bas).
10+4. **Comment le démarrer ?** Remplissez **Programme**, **Arguments** (sur une ligne, comme dans un terminal ; les guillemets regroupent des mots) et **Dossier de l'agent**. Le dossier est créé s'il n'existe pas.
11+5. Cliquez sur **Ajouter l'agent**. Sa conversation directe s'ouvre dès que la configuration est rechargée.
12+
13+Quand l'agent ne peut pas être ajouté (un nom déjà pris, par exemple), l'assistant dit pourquoi et n'écrit rien.
14+
15+## Variantes
16+
17+- **Sur cet ordinateur** : le programme tourne ici, dans le dossier de l'agent. Si Sidekicks, ouvert depuis le Finder, ne trouve pas le programme, indiquez son chemin complet (`which mm` dans un terminal le donne). Sidekicks lit le `PATH` de votre shell au démarrage : c'est rarement nécessaire.
18+- **Dans un conteneur Docker** : indiquez le **Nom du conteneur**. Le conteneur doit tourner, avec le dossier de l'agent monté au même chemin, et contenir le programme : voir [Lancer un agent dans un conteneur Docker](lancer-un-agent-dans-docker.md). Sidekicks démarre l'agent avec `docker exec -i <conteneur> <programme> <arguments>`.
19+- **Dans une sandbox sbx** : indiquez le **Nom de la sandbox**. Elle doit exister, avec le dossier de l'agent au même chemin : voir [Lancer un agent dans une sandbox sbx](lancer-un-agent-dans-une-sandbox.md). Sidekicks démarre l'agent avec `sbx exec -i <sandbox> <programme> <arguments>`.
20+- **Les options que l'assistant ne demande pas** (`env`, `afterStop`, un autre `workspacePath`) : ajoutez l'agent avec l'assistant, puis complétez son entrée dans **Settings**.
21+
22+## Voir aussi
23+
24+- Ce que l'assistant écrit : [Le fichier de configuration](../reference/configuration.md)
25+- [Modifier ou changer la configuration](modifier-ou-changer-la-configuration.md)
26+- [Tutoriel : votre premier agent, sans écrire de configuration](../tutorials/premier-agent.md)
added docs/fr/how-to/derouler-les-scenarios-de-bout-en-bout.md +102 -0
new file mode 100644
@@ -0,0 +1,102 @@
1+# Dérouler les scénarios de bout en bout (v0.1)
2+
3+Ce guide déroule les six scénarios qui valident une release avec les vrais agents de test (ticket #34), puis les vérifications de ce que la v0.1 a ajouté. Il suppose un Mac avec `mm`, llmman démarré, les modèles des quatre agents de test téléchargés, et Sidekicks construit depuis le tag de la release (`make package-macos`, ou `make dev`).
4+
5+Deux agents au plus tournent en même temps (`limits.maxRunningAgents: 2`). Entre deux scénarios, déchargez tous les modèles, pour que chaque scénario parte à froid :
6+
7+```bash
8+scripts/agents.sh unload --all
9+scripts/agents.sh ps # aucun modèle chargé
10+```
11+
12+Les libellés cités sont ceux de l'interface en anglais (la langue par défaut).
13+
14+## 0. Préparer
15+
16+1. `make install-test-config` (les quatre agents de test à l'emplacement par défaut ; le fichier précédent est gardé en sauvegarde).
17+2. Ouvrez Sidekicks. Sous **AGENTS** : Alice, Bob, Riker, Garfield, tous « stopped ».
18+3. Ouvrez le [rapport](#rapport) ci-dessous dans un éditeur de texte, pour le remplir au fur et à mesure.
19+
20+## 1. Alice — conversation simple, Markdown, annulation
21+
22+1. Cliquez sur **Alice (assistant)**, envoyez : `Donne un tableau markdown de 2 lignes (nom, rôle) puis un bloc de code Go qui affiche hello.`
23+2. Attendu : la réponse s'affiche au fil de l'eau ; un tableau et du code Go en couleurs avec **Copy**.
24+3. Envoyez : `Écris un poème de 40 vers sur la mer.` et cliquez sur **■ Stop** après quelques lignes.
25+4. Attendu : la réponse s'arrête et finit par « Answer stopped. » ; Alice repasse à « ready ».
26+
27+## 2. Alice + Bob — channel multi-agents, routage, contexte partagé
28+
29+1. Créez un channel `#e2e` (**⌘N**). Envoyez : `@alice donne un prénom pour un chat roux.`
30+2. Attendu : « Alice (assistant) joined the channel. » puis sa réponse avec un prénom.
31+3. Envoyez : `@bob que penses-tu du prénom proposé par Alice ?`
32+4. Attendu : Bob rejoint le channel et reprend le prénom donné par Alice : il a reçu les messages qu'il n'avait pas vus.
33+5. Envoyez un message sans mention : attendu, le dernier agent qui a parlé (Bob) répond.
34+
35+## 3. Riker — tool calls, permissions
36+
37+1. Déchargez les modèles. Cliquez sur **Riker (tools)**, envoyez : `Crée le fichier e2e.txt contenant "ok", puis liste le dossier.`
38+2. Attendu : une carte de permission en phrases claires. Cliquez sur **Allow once** : la carte de l'outil passe à ✓, `e2e.txt` est listé.
39+3. Envoyez : `Supprime e2e.txt.` Cliquez sur **Reject once**.
40+4. Attendu : Riker dit qu'il n'a pas pu ; le fichier est toujours là (📂 → Files).
41+
42+## 4. Garfield — commandes slash et skills
43+
44+1. Déchargez les modèles. Cliquez sur **Garfield (translator)**, tapez `/`.
45+2. Attendu : « Asking Garfield (translator) for their commands… » puis les commandes et skills de Garfield (`idioms`, `styles`…).
46+3. Choisissez `/idioms` (Tab), ajoutez `il pleut des cordes`, envoyez.
47+4. Attendu : la réponse explique l'expression avec un équivalent anglais.
48+
49+## 5. Garfield + Bob — `/garfield:commande` dans un channel
50+
51+1. Déchargez les modèles. Dans `#e2e`, invitez Garfield (**+ Invite**), puis tapez `/gar`.
52+2. Attendu : `/garfield:idioms`, `/garfield:styles`… listés avec leur agent.
53+3. Envoyez `/garfield:styles Bonjour à tous`. Attendu : seul Garfield répond.
54+4. Envoyez `@bob traduis la première version de Garfield en espagnol.` Attendu : Bob répond à partir du texte de Garfield.
55+
56+## 6. Alice — redémarrage et reprise
57+
58+1. Quittez Sidekicks (⌘Q) pendant que rien ne répond. Rouvrez-le.
59+2. Attendu : `#e2e` et les conversations directes sont là avec leurs messages ; les agents sont « stopped ».
60+3. Dans la conversation d'Alice, envoyez : `Quelle était ma première question ?`
61+4. Attendu : Alice répond à partir de l'échange précédent (sa session a été reprise, ou l'historique lui a été redonné).
62+
63+## 7. Ce que la v0.1 a ajouté
64+
65+| # | Vérification | Attendu |
66+| --- | --- | --- |
67+| 7.1 | `mv ~/Library/Application\ Support/sidekicks ~/sidekicks-backup`, ouvrez Sidekicks | Écran d'accueil : logo, trois étapes, **+ Add my first agent** |
68+| 7.2 | Ajoutez Alice avec l'assistant (programme `mm`, arguments `-acp -config agent.yaml`, dossier `agents/alice`) | Sa conversation s'ouvre ; Settings montre son entrée, les commentaires du fichier sont gardés |
69+| 7.3 | Ajoutez un agent dont le programme est `nothere`, démarrez-le (▶) | « The program "nothere" cannot be found… », avec **Technical details** replié |
70+| 7.4 | Ajoutez un agent dans un conteneur qui n'existe pas, démarrez-le | Une phrase sur le conteneur manquant |
71+| 7.5 | ⌘K, tapez `ali`, Entrée ; ⌘N ; ⌘L ; ⌘, ; ⌘J ; ⌘B ; ⇧⌘N | Chacun fait ce que dit [Raccourcis clavier](../reference/raccourcis-clavier.md) |
72+| 7.6 | Parcourez la fenêtre avec Tab | L'élément qui a le focus est toujours entouré |
73+| 7.7 | Ouvrez le `.app` installé depuis le `.dmg` **depuis le Finder**, démarrez Alice | Elle démarre : `mm` est trouvé sans chemin complet (le PATH du shell) |
74+| 7.8 | Remettez la sauvegarde : `rm -r ~/Library/Application\ Support/sidekicks && mv ~/sidekicks-backup ~/Library/Application\ Support/sidekicks` | Vos agents et votre historique sont de retour |
75+
76+## Rapport
77+
78+Copiez ceci dans un commentaire du ticket #34 et remplissez-le :
79+
80+```markdown
81+## Rapport de bout en bout — Sidekicks <version>, <date>
82+
83+Mac : <modèle, RAM>, macOS <version> · mm <version> · build : <dmg | make dev>
84+
85+| # | Scénario | Résultat | Notes |
86+| --- | --- | --- | --- |
87+| 1 | Alice : conversation, Markdown, annulation | ✅ / ❌ | |
88+| 2 | Alice + Bob : channel, routage, contexte partagé | ✅ / ❌ | |
89+| 3 | Riker : outils, autoriser / refuser | ✅ / ❌ | |
90+| 4 | Garfield : commandes / et skills | ✅ / ❌ | |
91+| 5 | Garfield + Bob : /garfield:commande dans un channel | ✅ / ❌ | |
92+| 6 | Alice : redémarrage et reprise | ✅ / ❌ | |
93+| 7 | Vérifications v0.1 (7.1–7.8) | ✅ / ❌ | |
94+
95+Modèles déchargés entre les scénarios : oui / non
96+```
97+
98+## Voir aussi
99+
100+- [Lancer les tests](lancer-les-tests.md) — les tests automatiques, avec et sans vrais agents
101+- [scripts/agents.sh](../reference/script-agents.md)
102+- [Publier une release](publier-une-release.md)
new file mode 100644
@@ -0,0 +1,102 @@
1+# Dérouler les scénarios de bout en bout (v0.1)
2+
3+Ce guide déroule les six scénarios qui valident une release avec les vrais agents de test (ticket #34), puis les vérifications de ce que la v0.1 a ajouté. Il suppose un Mac avec `mm`, llmman démarré, les modèles des quatre agents de test téléchargés, et Sidekicks construit depuis le tag de la release (`make package-macos`, ou `make dev`).
4+
5+Deux agents au plus tournent en même temps (`limits.maxRunningAgents: 2`). Entre deux scénarios, déchargez tous les modèles, pour que chaque scénario parte à froid :
6+
7+```bash
8+scripts/agents.sh unload --all
9+scripts/agents.sh ps # aucun modèle chargé
10+```
11+
12+Les libellés cités sont ceux de l'interface en anglais (la langue par défaut).
13+
14+## 0. Préparer
15+
16+1. `make install-test-config` (les quatre agents de test à l'emplacement par défaut ; le fichier précédent est gardé en sauvegarde).
17+2. Ouvrez Sidekicks. Sous **AGENTS** : Alice, Bob, Riker, Garfield, tous « stopped ».
18+3. Ouvrez le [rapport](#rapport) ci-dessous dans un éditeur de texte, pour le remplir au fur et à mesure.
19+
20+## 1. Alice — conversation simple, Markdown, annulation
21+
22+1. Cliquez sur **Alice (assistant)**, envoyez : `Donne un tableau markdown de 2 lignes (nom, rôle) puis un bloc de code Go qui affiche hello.`
23+2. Attendu : la réponse s'affiche au fil de l'eau ; un tableau et du code Go en couleurs avec **Copy**.
24+3. Envoyez : `Écris un poème de 40 vers sur la mer.` et cliquez sur **■ Stop** après quelques lignes.
25+4. Attendu : la réponse s'arrête et finit par « Answer stopped. » ; Alice repasse à « ready ».
26+
27+## 2. Alice + Bob — channel multi-agents, routage, contexte partagé
28+
29+1. Créez un channel `#e2e` (**⌘N**). Envoyez : `@alice donne un prénom pour un chat roux.`
30+2. Attendu : « Alice (assistant) joined the channel. » puis sa réponse avec un prénom.
31+3. Envoyez : `@bob que penses-tu du prénom proposé par Alice ?`
32+4. Attendu : Bob rejoint le channel et reprend le prénom donné par Alice : il a reçu les messages qu'il n'avait pas vus.
33+5. Envoyez un message sans mention : attendu, le dernier agent qui a parlé (Bob) répond.
34+
35+## 3. Riker — tool calls, permissions
36+
37+1. Déchargez les modèles. Cliquez sur **Riker (tools)**, envoyez : `Crée le fichier e2e.txt contenant "ok", puis liste le dossier.`
38+2. Attendu : une carte de permission en phrases claires. Cliquez sur **Allow once** : la carte de l'outil passe à ✓, `e2e.txt` est listé.
39+3. Envoyez : `Supprime e2e.txt.` Cliquez sur **Reject once**.
40+4. Attendu : Riker dit qu'il n'a pas pu ; le fichier est toujours là (📂 → Files).
41+
42+## 4. Garfield — commandes slash et skills
43+
44+1. Déchargez les modèles. Cliquez sur **Garfield (translator)**, tapez `/`.
45+2. Attendu : « Asking Garfield (translator) for their commands… » puis les commandes et skills de Garfield (`idioms`, `styles`…).
46+3. Choisissez `/idioms` (Tab), ajoutez `il pleut des cordes`, envoyez.
47+4. Attendu : la réponse explique l'expression avec un équivalent anglais.
48+
49+## 5. Garfield + Bob — `/garfield:commande` dans un channel
50+
51+1. Déchargez les modèles. Dans `#e2e`, invitez Garfield (**+ Invite**), puis tapez `/gar`.
52+2. Attendu : `/garfield:idioms`, `/garfield:styles`… listés avec leur agent.
53+3. Envoyez `/garfield:styles Bonjour à tous`. Attendu : seul Garfield répond.
54+4. Envoyez `@bob traduis la première version de Garfield en espagnol.` Attendu : Bob répond à partir du texte de Garfield.
55+
56+## 6. Alice — redémarrage et reprise
57+
58+1. Quittez Sidekicks (⌘Q) pendant que rien ne répond. Rouvrez-le.
59+2. Attendu : `#e2e` et les conversations directes sont là avec leurs messages ; les agents sont « stopped ».
60+3. Dans la conversation d'Alice, envoyez : `Quelle était ma première question ?`
61+4. Attendu : Alice répond à partir de l'échange précédent (sa session a été reprise, ou l'historique lui a été redonné).
62+
63+## 7. Ce que la v0.1 a ajouté
64+
65+| # | Vérification | Attendu |
66+| --- | --- | --- |
67+| 7.1 | `mv ~/Library/Application\ Support/sidekicks ~/sidekicks-backup`, ouvrez Sidekicks | Écran d'accueil : logo, trois étapes, **+ Add my first agent** |
68+| 7.2 | Ajoutez Alice avec l'assistant (programme `mm`, arguments `-acp -config agent.yaml`, dossier `agents/alice`) | Sa conversation s'ouvre ; Settings montre son entrée, les commentaires du fichier sont gardés |
69+| 7.3 | Ajoutez un agent dont le programme est `nothere`, démarrez-le (▶) | « The program "nothere" cannot be found… », avec **Technical details** replié |
70+| 7.4 | Ajoutez un agent dans un conteneur qui n'existe pas, démarrez-le | Une phrase sur le conteneur manquant |
71+| 7.5 | ⌘K, tapez `ali`, Entrée ; ⌘N ; ⌘L ; ⌘, ; ⌘J ; ⌘B ; ⇧⌘N | Chacun fait ce que dit [Raccourcis clavier](../reference/raccourcis-clavier.md) |
72+| 7.6 | Parcourez la fenêtre avec Tab | L'élément qui a le focus est toujours entouré |
73+| 7.7 | Ouvrez le `.app` installé depuis le `.dmg` **depuis le Finder**, démarrez Alice | Elle démarre : `mm` est trouvé sans chemin complet (le PATH du shell) |
74+| 7.8 | Remettez la sauvegarde : `rm -r ~/Library/Application\ Support/sidekicks && mv ~/sidekicks-backup ~/Library/Application\ Support/sidekicks` | Vos agents et votre historique sont de retour |
75+
76+## Rapport
77+
78+Copiez ceci dans un commentaire du ticket #34 et remplissez-le :
79+
80+```markdown
81+## Rapport de bout en bout — Sidekicks <version>, <date>
82+
83+Mac : <modèle, RAM>, macOS <version> · mm <version> · build : <dmg | make dev>
84+
85+| # | Scénario | Résultat | Notes |
86+| --- | --- | --- | --- |
87+| 1 | Alice : conversation, Markdown, annulation | ✅ / ❌ | |
88+| 2 | Alice + Bob : channel, routage, contexte partagé | ✅ / ❌ | |
89+| 3 | Riker : outils, autoriser / refuser | ✅ / ❌ | |
90+| 4 | Garfield : commandes / et skills | ✅ / ❌ | |
91+| 5 | Garfield + Bob : /garfield:commande dans un channel | ✅ / ❌ | |
92+| 6 | Alice : redémarrage et reprise | ✅ / ❌ | |
93+| 7 | Vérifications v0.1 (7.1–7.8) | ✅ / ❌ | |
94+
95+Modèles déchargés entre les scénarios : oui / non
96+```
97+
98+## Voir aussi
99+
100+- [Lancer les tests](lancer-les-tests.md) — les tests automatiques, avec et sans vrais agents
101+- [scripts/agents.sh](../reference/script-agents.md)
102+- [Publier une release](publier-une-release.md)
modified docs/fr/how-to/developper-l-application.md +2 -0
@@ -30,6 +30,8 @@ make build # bin/sidekicks
3030 make package # macOS : bin/sidekicks.app, signé ad hoc
3131 ```
3232
33+Les paquets de release vont dans `dist/` : `make package-macos`, `make package-linux`, `make package-windows` (voir [Publier une release](publier-une-release.md)).
34+
3335 ## Quand vous modifiez…
3436
3537 - **un service Go ou une charge d'événement** (`internal/app`, `internal/events`, `main.go`) : lancez `make bindings` et commitez `frontend/bindings` — la CI échoue s'ils ne sont pas à jour.
@@ -30,6 +30,8 @@ make build # bin/sidekicks
30 make package # macOS : bin/sidekicks.app, signé ad hoc30 make package # macOS : bin/sidekicks.app, signé ad hoc
31 ```31 ```
32 32
33+Les paquets de release vont dans `dist/` : `make package-macos`, `make package-linux`, `make package-windows` (voir [Publier une release](publier-une-release.md)).
34+
33 ## Quand vous modifiez…35 ## Quand vous modifiez…
34 36
35 - **un service Go ou une charge d'événement** (`internal/app`, `internal/events`, `main.go`) : lancez `make bindings` et commitez `frontend/bindings` — la CI échoue s'ils ne sont pas à jour.37 - **un service Go ou une charge d'événement** (`internal/app`, `internal/events`, `main.go`) : lancez `make bindings` et commitez `frontend/bindings` — la CI échoue s'ils ne sont pas à jour.
added docs/fr/how-to/installer-sidekicks.md +48 -0
new file mode 100644
@@ -0,0 +1,48 @@
1+# Installer Sidekicks
2+
3+Ce guide montre comment installer Sidekicks à partir des paquets d'une release, sous macOS, Linux ou Windows. Les paquets sont sur la page des releases du projet, `https://rickub.com/bots-garden/sidekicks/releases` ; `SHA256SUMS` donne leurs sommes de contrôle.
4+
5+Sidekicks discute avec des agents, il n'en contient pas. Installez aussi le programme de votre agent (par exemple `mm`).
6+
7+## macOS (Apple Silicon et Intel)
8+
9+Sidekicks demande macOS 13 (Ventura) ou plus récent.
10+
11+1. Téléchargez `Sidekicks-<version>-macos-universal.dmg` et ouvrez-le.
12+2. Glissez **sidekicks** dans **Applications**.
13+3. Ouvrez-le une fois depuis **Applications**. L'application n'est pas signée par un compte développeur Apple : macOS la refuse la première fois (« Apple n'a pas pu vérifier… »).
14+4. Ouvrez **Réglages Système → Confidentialité et sécurité**, descendez jusqu'au message sur Sidekicks, cliquez sur **Ouvrir quand même**, puis confirmez.
15+
16+Ensuite, Sidekicks s'ouvre normalement.
17+
18+## Linux (x86-64)
19+
20+Sidekicks a besoin de GTK 4 et de WebKitGTK 6 (Ubuntu 24.04 ou plus récent, Debian 13, Fedora 40 ou plus récent).
21+
22+- **Debian, Ubuntu** : `sudo apt install ./sidekicks_<version>_linux_amd64.deb`
23+- **Fedora** : `sudo dnf install ./sidekicks_<version>_linux_amd64.rpm`
24+- **Toute distribution, sans installer** : `chmod +x Sidekicks-<version>-x86_64.AppImage`, puis lancez-le. S'il se plaint de FUSE, lancez-le avec `--appimage-extract-and-run`.
25+- **Le programme seul** : `sidekicks_<version>_linux_amd64.tar.gz` contient `sidekicks` ; les bibliothèques GTK 4 et WebKitGTK 6 doivent être installées.
26+
27+Le `.deb` et le `.rpm` ajoutent Sidekicks au menu des applications.
28+
29+## Windows (x86-64)
30+
31+1. Téléchargez `Sidekicks-<version>-windows-amd64-installer.exe` et lancez-le.
32+2. L'installateur n'est pas signé : Windows SmartScreen peut afficher « Windows a protégé votre ordinateur ». Cliquez sur **Informations complémentaires**, puis **Exécuter quand même**.
33+3. L'installateur ajoute Microsoft Edge WebView2 s'il manque, puis un raccourci Sidekicks.
34+
35+`sidekicks_<version>_windows_amd64.zip` contient `sidekicks.exe` seul, pour une machine où WebView2 est déjà présent.
36+
37+Sous Windows, l'onglet **Terminal** d'un agent n'est pas encore disponible ; les fichiers, les conversations et les agents dans Docker fonctionnent.
38+
39+## Variantes
40+
41+- **Vérifier un téléchargement** : `shasum -a 256 <fichier>` (macOS) ou `sha256sum <fichier>` (Linux), à comparer avec `SHA256SUMS`.
42+- **macOS, depuis un terminal** : `xattr -dr com.apple.quarantine /Applications/sidekicks.app` lève la quarantaine, à la place de l'étape 4.
43+- **Le construire soi-même** : `make package-macos`, `make package-linux` ou `make package-windows` (voir [Publier une release](publier-une-release.md)).
44+
45+## Voir aussi
46+
47+- [Tutoriel : votre premier agent, sans écrire de configuration](../tutorials/premier-agent.md)
48+- [Repartir de zéro](repartir-de-zero.md)
new file mode 100644
@@ -0,0 +1,48 @@
1+# Installer Sidekicks
2+
3+Ce guide montre comment installer Sidekicks à partir des paquets d'une release, sous macOS, Linux ou Windows. Les paquets sont sur la page des releases du projet, `https://rickub.com/bots-garden/sidekicks/releases` ; `SHA256SUMS` donne leurs sommes de contrôle.
4+
5+Sidekicks discute avec des agents, il n'en contient pas. Installez aussi le programme de votre agent (par exemple `mm`).
6+
7+## macOS (Apple Silicon et Intel)
8+
9+Sidekicks demande macOS 13 (Ventura) ou plus récent.
10+
11+1. Téléchargez `Sidekicks-<version>-macos-universal.dmg` et ouvrez-le.
12+2. Glissez **sidekicks** dans **Applications**.
13+3. Ouvrez-le une fois depuis **Applications**. L'application n'est pas signée par un compte développeur Apple : macOS la refuse la première fois (« Apple n'a pas pu vérifier… »).
14+4. Ouvrez **Réglages Système → Confidentialité et sécurité**, descendez jusqu'au message sur Sidekicks, cliquez sur **Ouvrir quand même**, puis confirmez.
15+
16+Ensuite, Sidekicks s'ouvre normalement.
17+
18+## Linux (x86-64)
19+
20+Sidekicks a besoin de GTK 4 et de WebKitGTK 6 (Ubuntu 24.04 ou plus récent, Debian 13, Fedora 40 ou plus récent).
21+
22+- **Debian, Ubuntu** : `sudo apt install ./sidekicks_<version>_linux_amd64.deb`
23+- **Fedora** : `sudo dnf install ./sidekicks_<version>_linux_amd64.rpm`
24+- **Toute distribution, sans installer** : `chmod +x Sidekicks-<version>-x86_64.AppImage`, puis lancez-le. S'il se plaint de FUSE, lancez-le avec `--appimage-extract-and-run`.
25+- **Le programme seul** : `sidekicks_<version>_linux_amd64.tar.gz` contient `sidekicks` ; les bibliothèques GTK 4 et WebKitGTK 6 doivent être installées.
26+
27+Le `.deb` et le `.rpm` ajoutent Sidekicks au menu des applications.
28+
29+## Windows (x86-64)
30+
31+1. Téléchargez `Sidekicks-<version>-windows-amd64-installer.exe` et lancez-le.
32+2. L'installateur n'est pas signé : Windows SmartScreen peut afficher « Windows a protégé votre ordinateur ». Cliquez sur **Informations complémentaires**, puis **Exécuter quand même**.
33+3. L'installateur ajoute Microsoft Edge WebView2 s'il manque, puis un raccourci Sidekicks.
34+
35+`sidekicks_<version>_windows_amd64.zip` contient `sidekicks.exe` seul, pour une machine où WebView2 est déjà présent.
36+
37+Sous Windows, l'onglet **Terminal** d'un agent n'est pas encore disponible ; les fichiers, les conversations et les agents dans Docker fonctionnent.
38+
39+## Variantes
40+
41+- **Vérifier un téléchargement** : `shasum -a 256 <fichier>` (macOS) ou `sha256sum <fichier>` (Linux), à comparer avec `SHA256SUMS`.
42+- **macOS, depuis un terminal** : `xattr -dr com.apple.quarantine /Applications/sidekicks.app` lève la quarantaine, à la place de l'étape 4.
43+- **Le construire soi-même** : `make package-macos`, `make package-linux` ou `make package-windows` (voir [Publier une release](publier-une-release.md)).
44+
45+## Voir aussi
46+
47+- [Tutoriel : votre premier agent, sans écrire de configuration](../tutorials/premier-agent.md)
48+- [Repartir de zéro](repartir-de-zero.md)
modified docs/fr/how-to/lancer-les-tests.md +2 -2
@@ -22,8 +22,8 @@ La CI (`.github/workflows/ci.yml`) lance la même commande, plus `make bindings-
2222 | Pour vérifier | Commande |
2323 | --- | --- |
2424 | Les packages Go | `make test-go`, ou `go test -race ./internal/agents -run Limit -v` pour un test |
25-| Le frontend (vitest) | `make test-frontend` |
26-| L'outillage shell | `make test-scripts` |
25+| Le frontend (vitest), dont le contraste WCAG des couleurs du thème | `make test-frontend` |
26+| L'outillage shell (agents.sh, en-têtes de licence, noms des paquets, tag de release) | `make test-scripts` |
2727 | Le lint Go (golangci-lint, épinglé dans `tools/go.mod`) | `make lint-go` |
2828 | Les types du frontend et les avertissements Svelte | `make lint-frontend` |
2929 | Les en-têtes de licence | `make lint-headers` |
@@ -22,8 +22,8 @@ La CI (`.github/workflows/ci.yml`) lance la même commande, plus `make bindings-
22 | Pour vérifier | Commande |22 | Pour vérifier | Commande |
23 | --- | --- |23 | --- | --- |
24 | Les packages Go | `make test-go`, ou `go test -race ./internal/agents -run Limit -v` pour un test |24 | Les packages Go | `make test-go`, ou `go test -race ./internal/agents -run Limit -v` pour un test |
25-| Le frontend (vitest) | `make test-frontend` |25+| Le frontend (vitest), dont le contraste WCAG des couleurs du thème | `make test-frontend` |
26-| L'outillage shell | `make test-scripts` |26+| L'outillage shell (agents.sh, en-têtes de licence, noms des paquets, tag de release) | `make test-scripts` |
27 | Le lint Go (golangci-lint, épinglé dans `tools/go.mod`) | `make lint-go` |27 | Le lint Go (golangci-lint, épinglé dans `tools/go.mod`) | `make lint-go` |
28 | Les types du frontend et les avertissements Svelte | `make lint-frontend` |28 | Les types du frontend et les avertissements Svelte | `make lint-frontend` |
29 | Les en-têtes de licence | `make lint-headers` |29 | Les en-têtes de licence | `make lint-headers` |
added docs/fr/how-to/publier-une-release.md +23 -0
new file mode 100644
@@ -0,0 +1,23 @@
1+# Publier une release
2+
3+Ce guide montre comment publier une release de Sidekicks depuis un Mac : construire tous les paquets, tagger, attacher les fichiers à la release que Rickub crée. Il suppose les outils de [Développer l'application](developper-l-application.md), Docker Desktop démarré, `brew install makensis zip`, et le droit de pousser des tags. [RELEASING.fr.md](../../../RELEASING.fr.md) donne la même procédure en liste de contrôle.
4+
5+## Étapes
6+
7+1. **Fixez la version** dans `build/config.yml` (`info.version`), lancez `wails3 task common:update:build-assets`, remettez `LSMinimumSystemVersion` à `13.0.0` dans `build/darwin/Info.plist` et `Info.dev.plist`, puis commitez, fusionnez dans `main` et `git checkout main && git pull`.
8+2. **Construisez tout** : `make release-build`. `dist/` contient alors les paquets macOS (`.dmg` et `.zip` universels), Windows (installateur et `.zip`), Linux amd64 (`.deb`, `.rpm`, `.AppImage`, `.tar.gz`, construits dans Docker) et `SHA256SUMS`.
9+3. **Taggez** : `make release-tag`. Le tag `v<version>` est poussé, et le workflow **Release** crée la release sur Rickub avec des notes qui listent les fichiers.
10+4. **Attachez les fichiers** : sur Rickub, ouvrez **Releases → v<version>** et attachez tous les fichiers de `dist/`.
11+
12+## Variantes
13+
14+- **Linux arm64 aussi** : `LINUX_ARCHES="amd64 arm64" make release-build`.
15+- **Laisser un système de côté** : `SKIP="linux"` (ou `macos`, `windows`, plusieurs séparés par des espaces) avant `make release-build`.
16+- **Tagger sans pousser** : `scripts/tag-release.sh --no-push`, puis `git push origin v<version>` le moment venu.
17+- **Mauvais tag** : `git push origin :refs/tags/v<version>` et `git tag -d v<version>`, supprimez la release si elle a été créée, corrigez la version et taggez à nouveau.
18+- **Un seul paquet** : `make package-macos`, `make package-windows` ou `make package-linux` (Linux seulement, pour l'architecture de la machine).
19+
20+## Voir aussi
21+
22+- [Installer Sidekicks](installer-sidekicks.md)
23+- [Référence : commandes](../reference/commandes.md) — `make release-*`, `scripts/build-release.sh`, `scripts/tag-release.sh`, le workflow Release
new file mode 100644
@@ -0,0 +1,23 @@
1+# Publier une release
2+
3+Ce guide montre comment publier une release de Sidekicks depuis un Mac : construire tous les paquets, tagger, attacher les fichiers à la release que Rickub crée. Il suppose les outils de [Développer l'application](developper-l-application.md), Docker Desktop démarré, `brew install makensis zip`, et le droit de pousser des tags. [RELEASING.fr.md](../../../RELEASING.fr.md) donne la même procédure en liste de contrôle.
4+
5+## Étapes
6+
7+1. **Fixez la version** dans `build/config.yml` (`info.version`), lancez `wails3 task common:update:build-assets`, remettez `LSMinimumSystemVersion` à `13.0.0` dans `build/darwin/Info.plist` et `Info.dev.plist`, puis commitez, fusionnez dans `main` et `git checkout main && git pull`.
8+2. **Construisez tout** : `make release-build`. `dist/` contient alors les paquets macOS (`.dmg` et `.zip` universels), Windows (installateur et `.zip`), Linux amd64 (`.deb`, `.rpm`, `.AppImage`, `.tar.gz`, construits dans Docker) et `SHA256SUMS`.
9+3. **Taggez** : `make release-tag`. Le tag `v<version>` est poussé, et le workflow **Release** crée la release sur Rickub avec des notes qui listent les fichiers.
10+4. **Attachez les fichiers** : sur Rickub, ouvrez **Releases → v<version>** et attachez tous les fichiers de `dist/`.
11+
12+## Variantes
13+
14+- **Linux arm64 aussi** : `LINUX_ARCHES="amd64 arm64" make release-build`.
15+- **Laisser un système de côté** : `SKIP="linux"` (ou `macos`, `windows`, plusieurs séparés par des espaces) avant `make release-build`.
16+- **Tagger sans pousser** : `scripts/tag-release.sh --no-push`, puis `git push origin v<version>` le moment venu.
17+- **Mauvais tag** : `git push origin :refs/tags/v<version>` et `git tag -d v<version>`, supprimez la release si elle a été créée, corrigez la version et taggez à nouveau.
18+- **Un seul paquet** : `make package-macos`, `make package-windows` ou `make package-linux` (Linux seulement, pour l'architecture de la machine).
19+
20+## Voir aussi
21+
22+- [Installer Sidekicks](installer-sidekicks.md)
23+- [Référence : commandes](../reference/commandes.md) — `make release-*`, `scripts/build-release.sh`, `scripts/tag-release.sh`, le workflow Release
modified docs/fr/reference/commandes.md +47 -2
@@ -25,12 +25,17 @@ L'ordre est détaillé dans la [référence de la configuration](configuration.m
2525 | `dev` | `wails3 dev` : l'application avec rechargement à chaud. |
2626 | `build` | `wails3 build` : `bin/sidekicks`. |
2727 | `package` | `wails3 package` : sous macOS `bin/sidekicks.app`. |
28+| `package-linux` | `scripts/package.sh linux` : `.deb`, `.rpm`, `.AppImage` et `.tar.gz` pour l'architecture de la machine, dans `dist/`. Demande `file` (AppImage) et `rpm`. |
29+| `package-windows` | `scripts/package.sh windows` : installateur NSIS et `.zip` pour Windows amd64, compilés depuis Linux ou macOS, dans `dist/`. Demande `makensis` et `zip`. |
30+| `release-build` | `scripts/build-release.sh` : sur le Mac, tous les paquets de release dans `dist/` et `SHA256SUMS`. |
31+| `release-tag` | `scripts/tag-release.sh` : tague `v<version>` sur `main` et le pousse. |
32+| `package-macos` | `scripts/package.sh macos` : `.dmg` et `.zip` universels (arm64 + amd64, signés ad hoc), dans `dist/`. macOS seulement. |
2833 | `install-test-config` | `scripts/install-test-config.sh` : écrit `examples/test-agents.yaml` à l'emplacement par défaut de la configuration, avec des chemins absolus vers le dépôt : chaque agent est `mm -acp -config agent.yaml` lancé dans son dossier, avec `afterStop: [<dépôt>/scripts/agents.sh, unload, <agent>]` pour décharger son modèle à l'arrêt, `mm` écrit avec son chemin absolu (`MM_BIN`, ou celui du `PATH`), et le `PATH` courant dans l'`env` de chaque agent ; un fichier existant est gardé sous `config.yaml.<date>.bak`. |
2934 | `check` | `lint` puis `test`. |
3035 | `test` | `test-go`, `test-frontend`, `test-scripts`. |
3136 | `test-go` | `go test ./...` (construit `frontend/dist` d'abord s'il manque). |
3237 | `test-frontend` | `vitest run` dans `frontend/`. |
33-| `test-scripts` | `scripts/agents_test.sh` et `scripts/check-license-headers_test.sh`. |
38+| `test-scripts` | `scripts/agents_test.sh`, `scripts/check-license-headers_test.sh`, `scripts/package_test.sh` et `scripts/tag-release_test.sh`. |
3439 | `test-real` | `go test -tags realagents -count=1 ./...` — nécessite `mm` et llmman. |
3540 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |
3641 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |
@@ -46,6 +51,46 @@ scripts/check-license-headers.sh [racine du dépôt]
4651
4752 Liste les fichiers sources (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) connus de git dont les trois premières lignes ne contiennent pas `Sidekicks Source-Available License`, et se termine avec le code 1 s'il y en a. Non vérifiés : `frontend/bindings`, `build/` et `Taskfile.yml` de Wails, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.
4853
54+## `scripts/package.sh`
55+
56+```text
57+scripts/package.sh linux | windows | macos
58+scripts/package.sh names <système> <arch>
59+scripts/package.sh version
60+```
61+
62+| Commande | Effet |
63+| --- | --- |
64+| `linux`, `windows`, `macos` | Construit les paquets de ce système dans `dist/` (voir les cibles `package-*`), puis liste `dist/`. |
65+| `names <système> <arch>` | Affiche les noms des fichiers qu'un build produit, un par ligne ; ne construit rien. |
66+| `version` | Affiche `info.version` de `build/config.yml`. |
67+
68+| Système | Fichiers (`<v>` = version) |
69+| --- | --- |
70+| `linux` | `sidekicks_<v>_linux_<arch>.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-<x86_64\|aarch64>.AppImage` |
71+| `windows` | `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` |
72+| `macos` | `Sidekicks-<v>-macos-universal.dmg`, `Sidekicks-<v>-macos-universal.zip` |
73+
74+## `scripts/build-release.sh`
75+
76+```text
77+[LINUX_ARCHES="amd64 arm64"] [SKIP="macos windows linux"] scripts/build-release.sh
78+```
79+
80+Vide `dist/`, puis construit : macOS avec `scripts/package.sh macos` (macOS seulement), Windows avec `scripts/package.sh windows` (demande `makensis`), Linux avec `scripts/package.sh linux` dans un conteneur par architecture de `LINUX_ARCHES` (par défaut `amd64`), construit depuis `scripts/linux-packages.Dockerfile` (`ubuntu:26.04`, Go de `go.mod`, pnpm de `frontend/package.json`, CLI Wails) avec le dépôt monté sur `/src` et le cache des modules Go dans le volume `sidekicks-gomod-<arch>`. Écrit enfin `dist/SHA256SUMS`. `SKIP` laisse de côté les systèmes qu'il nomme. Échoue si `makensis` manque ou si Docker ne tourne pas, en nommant le système à laisser de côté.
81+
82+## `scripts/tag-release.sh`
83+
84+```text
85+scripts/tag-release.sh [--remote <nom>] [--no-push]
86+```
87+
88+Crée le tag annoté `v<version>` (`info.version` de `build/config.yml`, message `Sidekicks <version>`) et le pousse sur `--remote` (par défaut `origin`). Refuse si des fichiers suivis sont modifiés, si la branche courante n'est pas `main`, si `main` diffère de `<remote>/main` après un fetch, ou si le tag existe en local. Avertit, sans refuser, s'il manque dans `dist/` un paquet de la version ou `SHA256SUMS`. `--no-push` tague en local seulement.
89+
4990 ## CI
5091
51-`.github/workflows/ci.yml`, à chaque push et merge request : installe Go (version de `go.mod`), Node 22, pnpm (version de `frontend/package.json`), les paquets de build GTK 4 / WebKitGTK 6 et la CLI Wails, puis lance `make bindings-check` et `make check`.
92+`.github/workflows/ci.yml`, à chaque push et merge request, dans un conteneur `ubuntu:26.04` (GTK ≥ 4.10) : installe git, un compilateur C, les paquets de build GTK 4 / WebKitGTK 6, Node et npm depuis apt, Go depuis go.dev (version de `go.mod`), pnpm (version de `frontend/package.json`) et la CLI Wails, puis lance `make bindings-check` et `make check`.
93+
94+## Workflow de release
95+
96+`.github/workflows/release.yml`, au push d'un tag `v*`, avec `permissions: contents: write` : échoue si le tag n'est pas `v` + `info.version` de `build/config.yml`, puis crée la release `<tag>` nommée `Sidekicks <tag>` (`softprops/action-gh-release@v2`, via l'API compatible GitHub de Rickub derrière `GITHUB_API_URL`), avec des notes qui donnent le commit, les guides d'installation et le tableau des fichiers attendus (tirés de `scripts/package.sh names`). Il n'attache aucun fichier : les paquets sont attachés à la main.
@@ -25,12 +25,17 @@ L'ordre est détaillé dans la [référence de la configuration](configuration.m
25 | `dev` | `wails3 dev` : l'application avec rechargement à chaud. |25 | `dev` | `wails3 dev` : l'application avec rechargement à chaud. |
26 | `build` | `wails3 build` : `bin/sidekicks`. |26 | `build` | `wails3 build` : `bin/sidekicks`. |
27 | `package` | `wails3 package` : sous macOS `bin/sidekicks.app`. |27 | `package` | `wails3 package` : sous macOS `bin/sidekicks.app`. |
28+| `package-linux` | `scripts/package.sh linux` : `.deb`, `.rpm`, `.AppImage` et `.tar.gz` pour l'architecture de la machine, dans `dist/`. Demande `file` (AppImage) et `rpm`. |
29+| `package-windows` | `scripts/package.sh windows` : installateur NSIS et `.zip` pour Windows amd64, compilés depuis Linux ou macOS, dans `dist/`. Demande `makensis` et `zip`. |
30+| `release-build` | `scripts/build-release.sh` : sur le Mac, tous les paquets de release dans `dist/` et `SHA256SUMS`. |
31+| `release-tag` | `scripts/tag-release.sh` : tague `v<version>` sur `main` et le pousse. |
32+| `package-macos` | `scripts/package.sh macos` : `.dmg` et `.zip` universels (arm64 + amd64, signés ad hoc), dans `dist/`. macOS seulement. |
28 | `install-test-config` | `scripts/install-test-config.sh` : écrit `examples/test-agents.yaml` à l'emplacement par défaut de la configuration, avec des chemins absolus vers le dépôt : chaque agent est `mm -acp -config agent.yaml` lancé dans son dossier, avec `afterStop: [<dépôt>/scripts/agents.sh, unload, <agent>]` pour décharger son modèle à l'arrêt, `mm` écrit avec son chemin absolu (`MM_BIN`, ou celui du `PATH`), et le `PATH` courant dans l'`env` de chaque agent ; un fichier existant est gardé sous `config.yaml.<date>.bak`. |33 | `install-test-config` | `scripts/install-test-config.sh` : écrit `examples/test-agents.yaml` à l'emplacement par défaut de la configuration, avec des chemins absolus vers le dépôt : chaque agent est `mm -acp -config agent.yaml` lancé dans son dossier, avec `afterStop: [<dépôt>/scripts/agents.sh, unload, <agent>]` pour décharger son modèle à l'arrêt, `mm` écrit avec son chemin absolu (`MM_BIN`, ou celui du `PATH`), et le `PATH` courant dans l'`env` de chaque agent ; un fichier existant est gardé sous `config.yaml.<date>.bak`. |
29 | `check` | `lint` puis `test`. |34 | `check` | `lint` puis `test`. |
30 | `test` | `test-go`, `test-frontend`, `test-scripts`. |35 | `test` | `test-go`, `test-frontend`, `test-scripts`. |
31 | `test-go` | `go test ./...` (construit `frontend/dist` d'abord s'il manque). |36 | `test-go` | `go test ./...` (construit `frontend/dist` d'abord s'il manque). |
32 | `test-frontend` | `vitest run` dans `frontend/`. |37 | `test-frontend` | `vitest run` dans `frontend/`. |
33-| `test-scripts` | `scripts/agents_test.sh` et `scripts/check-license-headers_test.sh`. |38+| `test-scripts` | `scripts/agents_test.sh`, `scripts/check-license-headers_test.sh`, `scripts/package_test.sh` et `scripts/tag-release_test.sh`. |
34 | `test-real` | `go test -tags realagents -count=1 ./...` — nécessite `mm` et llmman. |39 | `test-real` | `go test -tags realagents -count=1 ./...` — nécessite `mm` et llmman. |
35 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |40 | `lint` | `lint-go`, `lint-frontend`, `lint-headers`. |
36 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |41 | `lint-go` | golangci-lint v2.14.0 (`tools/go.mod`), configuration `.golangci.yml`. |
@@ -46,6 +51,46 @@ scripts/check-license-headers.sh [racine du dépôt]
46 51
47 Liste les fichiers sources (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) connus de git dont les trois premières lignes ne contiennent pas `Sidekicks Source-Available License`, et se termine avec le code 1 s'il y en a. Non vérifiés : `frontend/bindings`, `build/` et `Taskfile.yml` de Wails, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.52 Liste les fichiers sources (`.go .ts .js .svelte .css .html .sh .py .yml .yaml`, `Makefile`) connus de git dont les trois premières lignes ne contiennent pas `Sidekicks Source-Available License`, et se termine avec le code 1 s'il y en a. Non vérifiés : `frontend/bindings`, `build/` et `Taskfile.yml` de Wails, `*.d.ts`, `pnpm-lock.yaml`, `vite.config.ts`, `svelte.config.js`, `agents/`, `*/testdata/*`, `git.sh`, `new.*.sh`, `tag-step.sh`, `.claude/`, `.tickets/`.
48 53
54+## `scripts/package.sh`
55+
56+```text
57+scripts/package.sh linux | windows | macos
58+scripts/package.sh names <système> <arch>
59+scripts/package.sh version
60+```
61+
62+| Commande | Effet |
63+| --- | --- |
64+| `linux`, `windows`, `macos` | Construit les paquets de ce système dans `dist/` (voir les cibles `package-*`), puis liste `dist/`. |
65+| `names <système> <arch>` | Affiche les noms des fichiers qu'un build produit, un par ligne ; ne construit rien. |
66+| `version` | Affiche `info.version` de `build/config.yml`. |
67+
68+| Système | Fichiers (`<v>` = version) |
69+| --- | --- |
70+| `linux` | `sidekicks_<v>_linux_<arch>.deb`, `.rpm`, `.tar.gz`, `Sidekicks-<v>-<x86_64\|aarch64>.AppImage` |
71+| `windows` | `Sidekicks-<v>-windows-amd64-installer.exe`, `sidekicks_<v>_windows_amd64.zip` |
72+| `macos` | `Sidekicks-<v>-macos-universal.dmg`, `Sidekicks-<v>-macos-universal.zip` |
73+
74+## `scripts/build-release.sh`
75+
76+```text
77+[LINUX_ARCHES="amd64 arm64"] [SKIP="macos windows linux"] scripts/build-release.sh
78+```
79+
80+Vide `dist/`, puis construit : macOS avec `scripts/package.sh macos` (macOS seulement), Windows avec `scripts/package.sh windows` (demande `makensis`), Linux avec `scripts/package.sh linux` dans un conteneur par architecture de `LINUX_ARCHES` (par défaut `amd64`), construit depuis `scripts/linux-packages.Dockerfile` (`ubuntu:26.04`, Go de `go.mod`, pnpm de `frontend/package.json`, CLI Wails) avec le dépôt monté sur `/src` et le cache des modules Go dans le volume `sidekicks-gomod-<arch>`. Écrit enfin `dist/SHA256SUMS`. `SKIP` laisse de côté les systèmes qu'il nomme. Échoue si `makensis` manque ou si Docker ne tourne pas, en nommant le système à laisser de côté.
81+
82+## `scripts/tag-release.sh`
83+
84+```text
85+scripts/tag-release.sh [--remote <nom>] [--no-push]
86+```
87+
88+Crée le tag annoté `v<version>` (`info.version` de `build/config.yml`, message `Sidekicks <version>`) et le pousse sur `--remote` (par défaut `origin`). Refuse si des fichiers suivis sont modifiés, si la branche courante n'est pas `main`, si `main` diffère de `<remote>/main` après un fetch, ou si le tag existe en local. Avertit, sans refuser, s'il manque dans `dist/` un paquet de la version ou `SHA256SUMS`. `--no-push` tague en local seulement.
89+
49 ## CI90 ## CI
50 91
51-`.github/workflows/ci.yml`, à chaque push et merge request : installe Go (version de `go.mod`), Node 22, pnpm (version de `frontend/package.json`), les paquets de build GTK 4 / WebKitGTK 6 et la CLI Wails, puis lance `make bindings-check` et `make check`.92+`.github/workflows/ci.yml`, à chaque push et merge request, dans un conteneur `ubuntu:26.04` (GTK ≥ 4.10) : installe git, un compilateur C, les paquets de build GTK 4 / WebKitGTK 6, Node et npm depuis apt, Go depuis go.dev (version de `go.mod`), pnpm (version de `frontend/package.json`) et la CLI Wails, puis lance `make bindings-check` et `make check`.
93+
94+## Workflow de release
95+
96+`.github/workflows/release.yml`, au push d'un tag `v*`, avec `permissions: contents: write` : échoue si le tag n'est pas `v` + `info.version` de `build/config.yml`, puis crée la release `<tag>` nommée `Sidekicks <tag>` (`softprops/action-gh-release@v2`, via l'API compatible GitHub de Rickub derrière `GITHUB_API_URL`), avec des notes qui donnent le commit, les guides d'installation et le tableau des fichiers attendus (tirés de `scripts/package.sh names`). Il n'attache aucun fichier : les paquets sont attachés à la main.
modified docs/fr/reference/configuration.md +14 -0
@@ -94,6 +94,20 @@ Le runtime indique à Sidekicks comment atteindre les **fichiers et le terminal*
9494
9595 Un channel déclaré ici est créé au démarrage, ou au rechargement du fichier, **si aucun channel ne porte encore ce nom** (sans tenir compte de la casse) ; il vit ensuite dans l'application comme n'importe quel channel, et peut y être renommé ou supprimé. Le retirer du fichier ne le supprime pas (supprimer un historique doit être une action explicite de l'utilisateur). Chaque fichier de configuration a ses propres channels et son propre historique ([où il est gardé](agents-et-evenements.md#historique)).
9696
97+## Ce qu'écrit l'assistant « Ajouter un agent »
98+
99+L'assistant ajoute une entrée à la fin de `agents` ; le reste du fichier, commentaires compris, est gardé. Rien n'est écrit si le résultat ne serait pas valide.
100+
101+| Champ de l'assistant | Écrit comme |
102+| --- | --- |
103+| Nom utilisé avec @, Nom affiché, Icône, Ce qu'il fait | `name`, `title`, `icon`, `description` (champs vides omis) |
104+| Dossier de l'agent | `workspace`, tel que saisi (`~` et chemins relatifs résolus comme tout `workspace`) |
105+| Sur cet ordinateur | `command: <programme>`, `args: [<arguments>]` |
106+| Dans un conteneur Docker | `command: docker`, `args: [exec, -i, <conteneur>, <programme>, <arguments>]`, `runtime: {type: container, container: <conteneur>}` |
107+| Dans une sandbox sbx | `command: sbx`, `args: [exec, -i, <sandbox>, <programme>, <arguments>]`, `runtime: {type: sbx, sandbox: <sandbox>}` |
108+
109+Les arguments sont découpés comme le ferait un shell dans les cas simples : les espaces séparent, les guillemets simples ou doubles regroupent.
110+
97111 ## Règles vérifiées par le chargeur, pas par le schéma
98112
99113 JSON Schema ne sait pas les exprimer ; le chargeur de configuration (issue #9) les vérifie et les signale comme les erreurs de schéma :
@@ -94,6 +94,20 @@ Le runtime indique à Sidekicks comment atteindre les **fichiers et le terminal*
94 94
95 Un channel déclaré ici est créé au démarrage, ou au rechargement du fichier, **si aucun channel ne porte encore ce nom** (sans tenir compte de la casse) ; il vit ensuite dans l'application comme n'importe quel channel, et peut y être renommé ou supprimé. Le retirer du fichier ne le supprime pas (supprimer un historique doit être une action explicite de l'utilisateur). Chaque fichier de configuration a ses propres channels et son propre historique ([où il est gardé](agents-et-evenements.md#historique)).95 Un channel déclaré ici est créé au démarrage, ou au rechargement du fichier, **si aucun channel ne porte encore ce nom** (sans tenir compte de la casse) ; il vit ensuite dans l'application comme n'importe quel channel, et peut y être renommé ou supprimé. Le retirer du fichier ne le supprime pas (supprimer un historique doit être une action explicite de l'utilisateur). Chaque fichier de configuration a ses propres channels et son propre historique ([où il est gardé](agents-et-evenements.md#historique)).
96 96
97+## Ce qu'écrit l'assistant « Ajouter un agent »
98+
99+L'assistant ajoute une entrée à la fin de `agents` ; le reste du fichier, commentaires compris, est gardé. Rien n'est écrit si le résultat ne serait pas valide.
100+
101+| Champ de l'assistant | Écrit comme |
102+| --- | --- |
103+| Nom utilisé avec @, Nom affiché, Icône, Ce qu'il fait | `name`, `title`, `icon`, `description` (champs vides omis) |
104+| Dossier de l'agent | `workspace`, tel que saisi (`~` et chemins relatifs résolus comme tout `workspace`) |
105+| Sur cet ordinateur | `command: <programme>`, `args: [<arguments>]` |
106+| Dans un conteneur Docker | `command: docker`, `args: [exec, -i, <conteneur>, <programme>, <arguments>]`, `runtime: {type: container, container: <conteneur>}` |
107+| Dans une sandbox sbx | `command: sbx`, `args: [exec, -i, <sandbox>, <programme>, <arguments>]`, `runtime: {type: sbx, sandbox: <sandbox>}` |
108+
109+Les arguments sont découpés comme le ferait un shell dans les cas simples : les espaces séparent, les guillemets simples ou doubles regroupent.
110+
97 ## Règles vérifiées par le chargeur, pas par le schéma111 ## Règles vérifiées par le chargeur, pas par le schéma
98 112
99 JSON Schema ne sait pas les exprimer ; le chargeur de configuration (issue #9) les vérifie et les signale comme les erreurs de schéma :113 JSON Schema ne sait pas les exprimer ; le chargeur de configuration (issue #9) les vérifie et les signale comme les erreurs de schéma :
added docs/fr/reference/raccourcis-clavier.md +51 -0
new file mode 100644
@@ -0,0 +1,51 @@
1+# Référence : raccourcis clavier
2+
3+Sous macOS, les raccourcis utilisent **⌘** ; sous Linux et Windows, **Ctrl**. La table est `frontend/src/lib/shortcuts.ts`.
4+
5+## Application
6+
7+| macOS | Linux, Windows | Action |
8+| --- | --- | --- |
9+| ⌘K | Ctrl+K | Ouvre ou ferme la palette de commandes |
10+| ⌘N | Ctrl+N | Nouveau channel (ouvre le dock des channels s'il est replié) |
11+| ⇧⌘N | Ctrl+Maj+N | Ajouter un agent (l'assistant) |
12+| ⌘L | Ctrl+L | Place le curseur dans la zone de message |
13+| ⌘B | Ctrl+B | Affiche ou masque le dock des channels et des agents |
14+| ⌘, | Ctrl+, | Affiche ou masque Settings |
15+| ⌘J | Ctrl+J | Affiche ou masque le journal |
16+
17+Sous Linux et Windows, quand le curseur est dans le terminal d'un agent, le terminal garde les raccourcis Ctrl (Ctrl+L efface l'écran, Ctrl+K coupe la ligne…).
18+
19+## Palette de commandes
20+
21+| Touche | Action |
22+| --- | --- |
23+| lettres | Filtre : channels (`#` facultatif), conversations des agents, fichiers des agents, actions |
24+| ↑ ↓ | Déplace la sélection |
25+| Entrée | Lance la sélection |
26+| Échap, ou un clic à côté | Ferme la palette |
27+
28+La palette liste, dans l'ordre : les channels, une conversation par agent, les fichiers de chaque agent, les actions du tableau ci-dessus, et **Ouvrir une configuration…**.
29+
30+## Zone de message
31+
32+| Touche | Action |
33+| --- | --- |
34+| Entrée | Envoie |
35+| Maj+Entrée | Nouvelle ligne |
36+| ↑ (zone vide) | Messages précédents |
37+| `@`, `/` | Listes des agents et des commandes ; Tab ou Entrée choisit, ↑ ↓ déplacent, Échap ferme |
38+
39+## Éditeurs (Settings, fichiers des agents)
40+
41+| macOS | Linux, Windows | Action |
42+| --- | --- | --- |
43+| ⌘S | Ctrl+S | Enregistre |
44+
45+## Assistant, liste des channels
46+
47+| Touche | Action |
48+| --- | --- |
49+| Entrée | Valide le nom d'un channel nouveau ou renommé |
50+| Échap | L'annule |
51+| Tab | Passe d'un champ à l'autre ; chaque bouton est atteignable, et l'élément qui a le focus est entouré |
new file mode 100644
@@ -0,0 +1,51 @@
1+# Référence : raccourcis clavier
2+
3+Sous macOS, les raccourcis utilisent **⌘** ; sous Linux et Windows, **Ctrl**. La table est `frontend/src/lib/shortcuts.ts`.
4+
5+## Application
6+
7+| macOS | Linux, Windows | Action |
8+| --- | --- | --- |
9+| ⌘K | Ctrl+K | Ouvre ou ferme la palette de commandes |
10+| ⌘N | Ctrl+N | Nouveau channel (ouvre le dock des channels s'il est replié) |
11+| ⇧⌘N | Ctrl+Maj+N | Ajouter un agent (l'assistant) |
12+| ⌘L | Ctrl+L | Place le curseur dans la zone de message |
13+| ⌘B | Ctrl+B | Affiche ou masque le dock des channels et des agents |
14+| ⌘, | Ctrl+, | Affiche ou masque Settings |
15+| ⌘J | Ctrl+J | Affiche ou masque le journal |
16+
17+Sous Linux et Windows, quand le curseur est dans le terminal d'un agent, le terminal garde les raccourcis Ctrl (Ctrl+L efface l'écran, Ctrl+K coupe la ligne…).
18+
19+## Palette de commandes
20+
21+| Touche | Action |
22+| --- | --- |
23+| lettres | Filtre : channels (`#` facultatif), conversations des agents, fichiers des agents, actions |
24+| ↑ ↓ | Déplace la sélection |
25+| Entrée | Lance la sélection |
26+| Échap, ou un clic à côté | Ferme la palette |
27+
28+La palette liste, dans l'ordre : les channels, une conversation par agent, les fichiers de chaque agent, les actions du tableau ci-dessus, et **Ouvrir une configuration…**.
29+
30+## Zone de message
31+
32+| Touche | Action |
33+| --- | --- |
34+| Entrée | Envoie |
35+| Maj+Entrée | Nouvelle ligne |
36+| ↑ (zone vide) | Messages précédents |
37+| `@`, `/` | Listes des agents et des commandes ; Tab ou Entrée choisit, ↑ ↓ déplacent, Échap ferme |
38+
39+## Éditeurs (Settings, fichiers des agents)
40+
41+| macOS | Linux, Windows | Action |
42+| --- | --- | --- |
43+| ⌘S | Ctrl+S | Enregistre |
44+
45+## Assistant, liste des channels
46+
47+| Touche | Action |
48+| --- | --- |
49+| Entrée | Valide le nom d'un channel nouveau ou renommé |
50+| Échap | L'annule |
51+| Tab | Passe d'un champ à l'autre ; chaque bouton est atteignable, et l'élément qui a le focus est entouré |
added docs/fr/tutorials/premier-agent.md +66 -0
new file mode 100644
@@ -0,0 +1,66 @@
1+# Tutoriel : votre premier agent, sans écrire de configuration
2+
3+À la fin de ce tutoriel, vous aurez ajouté un agent à Sidekicks avec l'assistant « Ajouter un agent », discuté avec lui, et l'aurez retrouvé avec la palette de commandes. Vous n'ouvrirez pas une seule fois le fichier de configuration.
4+
5+## Prérequis
6+
7+- Sidekicks installé : voir [Installer Sidekicks](../how-to/installer-sidekicks.md).
8+- `mm` installé, et llmman démarré avec le modèle d'Alice téléchargé (voir [Votre premier échange ACP avec un agent de test](premier-echange-acp.md)).
9+- Le dépôt de Sidekicks sur votre ordinateur : le dossier d'Alice, `agents/alice`, contient son `agent.yaml`. Ci-dessous, c'est `~/sidekicks/agents/alice` ; utilisez votre propre chemin.
10+
11+L'interface est en anglais par défaut ; avec `ui.language: fr`, elle est en français. Les libellés ci-dessous sont ceux de l'interface en français.
12+
13+## Étape 1 — Ouvrir Sidekicks
14+
15+Ouvrez Sidekicks depuis votre dossier Applications (ou votre menu d'applications).
16+
17+Vous devez voir le logo de Sidekicks, « Bienvenue dans Sidekicks », trois étapes numérotées et un bouton bleu **+ Ajouter mon premier agent**. Si des agents sont déjà listés à gauche, utilisez plutôt **+ Ajouter un agent** sous la liste : la suite est identique.
18+
19+## Étape 2 — Dire qui est l'agent
20+
21+Cliquez sur **+ Ajouter mon premier agent**. Dans **Nom affiché**, tapez :
22+
23+```text
24+Alice (assistante)
25+```
26+
27+Vous devez voir **Nom utilisé avec @** se remplir tout seul avec `alice-assistante`. Remplacez-le par `alice` : c'est le nom avec lequel vous l'appellerez.
28+
29+## Étape 3 — Dire comment le démarrer
30+
31+Laissez **Sur cet ordinateur** sélectionné. Remplissez :
32+
33+| Champ | Valeur |
34+| --- | --- |
35+| Programme | `mm` |
36+| Arguments | `-acp -config agent.yaml` |
37+| Dossier de l'agent | `~/sidekicks/agents/alice` (ou **Choisir…** puis le dossier) |
38+
39+Cliquez sur **Ajouter l'agent**.
40+
41+Vous devez voir l'assistant se fermer, **🤖 Alice (assistante)** apparaître sous **AGENTS** et sous **MESSAGES DIRECTS**, et sa conversation s'ouvrir au centre avec « Écrivez votre premier message à Alice (assistante). »
42+
43+## Étape 4 — Discuter avec Alice
44+
45+Cliquez dans la zone de texte en bas, tapez ceci et appuyez sur Entrée :
46+
47+```text
48+Bonjour Alice, présente-toi en une phrase.
49+```
50+
51+Vous devez voir la pastille d'Alice passer à l'orange pendant qu'elle démarre et répond, puis sa phrase s'afficher mot à mot.
52+
53+## Étape 5 — La retrouver au clavier
54+
55+Cliquez sur **# general** à gauche. Puis appuyez sur **⌘K** (Ctrl+K sous Linux et Windows) et tapez `ali`.
56+
57+Vous devez voir une liste avec **Alice (assistante)** en premier. Appuyez sur Entrée : sa conversation revient au centre.
58+
59+## Et maintenant ?
60+
61+Vous avez ajouté un agent sans toucher au YAML, discuté avec lui et circulé au clavier. Pour aller plus loin :
62+
63+- Pour ajouter un agent qui tourne dans Docker ou dans une sandbox sbx → [Ajouter un agent](../how-to/ajouter-un-agent.md)
64+- Pour réunir plusieurs agents dans un channel → [Un channel avec deux agents](premier-channel.md)
65+- Tous les raccourcis → [Raccourcis clavier](../reference/raccourcis-clavier.md)
66+- Ce que l'assistant a écrit → [Le fichier de configuration](../reference/configuration.md)
new file mode 100644
@@ -0,0 +1,66 @@
1+# Tutoriel : votre premier agent, sans écrire de configuration
2+
3+À la fin de ce tutoriel, vous aurez ajouté un agent à Sidekicks avec l'assistant « Ajouter un agent », discuté avec lui, et l'aurez retrouvé avec la palette de commandes. Vous n'ouvrirez pas une seule fois le fichier de configuration.
4+
5+## Prérequis
6+
7+- Sidekicks installé : voir [Installer Sidekicks](../how-to/installer-sidekicks.md).
8+- `mm` installé, et llmman démarré avec le modèle d'Alice téléchargé (voir [Votre premier échange ACP avec un agent de test](premier-echange-acp.md)).
9+- Le dépôt de Sidekicks sur votre ordinateur : le dossier d'Alice, `agents/alice`, contient son `agent.yaml`. Ci-dessous, c'est `~/sidekicks/agents/alice` ; utilisez votre propre chemin.
10+
11+L'interface est en anglais par défaut ; avec `ui.language: fr`, elle est en français. Les libellés ci-dessous sont ceux de l'interface en français.
12+
13+## Étape 1 — Ouvrir Sidekicks
14+
15+Ouvrez Sidekicks depuis votre dossier Applications (ou votre menu d'applications).
16+
17+Vous devez voir le logo de Sidekicks, « Bienvenue dans Sidekicks », trois étapes numérotées et un bouton bleu **+ Ajouter mon premier agent**. Si des agents sont déjà listés à gauche, utilisez plutôt **+ Ajouter un agent** sous la liste : la suite est identique.
18+
19+## Étape 2 — Dire qui est l'agent
20+
21+Cliquez sur **+ Ajouter mon premier agent**. Dans **Nom affiché**, tapez :
22+
23+```text
24+Alice (assistante)
25+```
26+
27+Vous devez voir **Nom utilisé avec @** se remplir tout seul avec `alice-assistante`. Remplacez-le par `alice` : c'est le nom avec lequel vous l'appellerez.
28+
29+## Étape 3 — Dire comment le démarrer
30+
31+Laissez **Sur cet ordinateur** sélectionné. Remplissez :
32+
33+| Champ | Valeur |
34+| --- | --- |
35+| Programme | `mm` |
36+| Arguments | `-acp -config agent.yaml` |
37+| Dossier de l'agent | `~/sidekicks/agents/alice` (ou **Choisir…** puis le dossier) |
38+
39+Cliquez sur **Ajouter l'agent**.
40+
41+Vous devez voir l'assistant se fermer, **🤖 Alice (assistante)** apparaître sous **AGENTS** et sous **MESSAGES DIRECTS**, et sa conversation s'ouvrir au centre avec « Écrivez votre premier message à Alice (assistante). »
42+
43+## Étape 4 — Discuter avec Alice
44+
45+Cliquez dans la zone de texte en bas, tapez ceci et appuyez sur Entrée :
46+
47+```text
48+Bonjour Alice, présente-toi en une phrase.
49+```
50+
51+Vous devez voir la pastille d'Alice passer à l'orange pendant qu'elle démarre et répond, puis sa phrase s'afficher mot à mot.
52+
53+## Étape 5 — La retrouver au clavier
54+
55+Cliquez sur **# general** à gauche. Puis appuyez sur **⌘K** (Ctrl+K sous Linux et Windows) et tapez `ali`.
56+
57+Vous devez voir une liste avec **Alice (assistante)** en premier. Appuyez sur Entrée : sa conversation revient au centre.
58+
59+## Et maintenant ?
60+
61+Vous avez ajouté un agent sans toucher au YAML, discuté avec lui et circulé au clavier. Pour aller plus loin :
62+
63+- Pour ajouter un agent qui tourne dans Docker ou dans une sandbox sbx → [Ajouter un agent](../how-to/ajouter-un-agent.md)
64+- Pour réunir plusieurs agents dans un channel → [Un channel avec deux agents](premier-channel.md)
65+- Tous les raccourcis → [Raccourcis clavier](../reference/raccourcis-clavier.md)
66+- Ce que l'assistant a écrit → [Le fichier de configuration](../reference/configuration.md)
modified docs/fr/tutorials/premiere-conversation.md +2 -0
@@ -27,6 +27,8 @@ Vous devriez voir votre message, puis « Alice … writing… » : la réponse a
2727
2828 Cliquez sur **Copy** : il affiche « Copied ✓ », le code est dans votre presse-papiers.
2929
30+Passez maintenant le pointeur sur la réponse d'Alice : **⧉ Copy** apparaît à côté de son nom. Cliquez dessus : toute la réponse est dans votre presse-papiers, en Markdown (tableau et code compris).
31+
3032 ## Étape 3 — Laisser Riker exécuter une commande
3133
3234 Cliquez sur **Riker (tools)** et envoyez :
@@ -27,6 +27,8 @@ Vous devriez voir votre message, puis « Alice … writing… » : la réponse a
27 27
28 Cliquez sur **Copy** : il affiche « Copied ✓ », le code est dans votre presse-papiers.28 Cliquez sur **Copy** : il affiche « Copied ✓ », le code est dans votre presse-papiers.
29 29
30+Passez maintenant le pointeur sur la réponse d'Alice : **⧉ Copy** apparaît à côté de son nom. Cliquez dessus : toute la réponse est dans votre presse-papiers, en Markdown (tableau et code compris).
31+
30 ## Étape 3 — Laisser Riker exécuter une commande32 ## Étape 3 — Laisser Riker exécuter une commande
31 33
32 Cliquez sur **Riker (tools)** et envoyez :34 Cliquez sur **Riker (tools)** et envoyez :
modified frontend/bindings/git.rickub.com/bots-garden/sidekicks/internal/app/configservice.ts +22 -0
@@ -16,6 +16,19 @@ import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wails
1616 // @ts-ignore: Unused imports
1717 import * as $models from "./models.js";
1818
19+/**
20+ * AddAgent adds the agent described by form to the configuration file,
21+ * keeping its comments, and creates the agent's folder when it does not
22+ * exist. When the result would not be valid (a name already used, a name
23+ * with spaces…), nothing is written and the problems are returned. The file
24+ * watcher then reloads the configuration, as after a save in Settings.
25+ *
26+ * problems, err := (&ConfigService{Core: core}).AddAgent(AgentForm{Name: "alice", Program: "mm", Workspace: "~/agents/alice"})
27+ */
28+export function AddAgent(form: $models.AgentForm): $CancellablePromise<string[] | null> {
29+ return $Call.ByID(698361705, form);
30+}
31+
1932 /**
2033 * Choose asks the user for a configuration file and opens it. The dialog
2134 * starts in the folder of the configuration in use: on macOS the default one
@@ -26,6 +39,15 @@ export function Choose(): $CancellablePromise<string> {
2639 return $Call.ByID(1890400804);
2740 }
2841
42+/**
43+ * ChooseFolder asks the user for a folder, starting in their home folder,
44+ * for the assistant's "agent's folder" field. It returns "" when the user
45+ * cancelled.
46+ */
47+export function ChooseFolder(): $CancellablePromise<string> {
48+ return $Call.ByID(1715751888);
49+}
50+
2951 /**
3052 * Content returns the text of the configuration file, for the editor.
3153 */
@@ -16,6 +16,19 @@ import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wails
16 // @ts-ignore: Unused imports16 // @ts-ignore: Unused imports
17 import * as $models from "./models.js";17 import * as $models from "./models.js";
18 18
19+/**
20+ * AddAgent adds the agent described by form to the configuration file,
21+ * keeping its comments, and creates the agent's folder when it does not
22+ * exist. When the result would not be valid (a name already used, a name
23+ * with spaces…), nothing is written and the problems are returned. The file
24+ * watcher then reloads the configuration, as after a save in Settings.
25+ *
26+ * problems, err := (&ConfigService{Core: core}).AddAgent(AgentForm{Name: "alice", Program: "mm", Workspace: "~/agents/alice"})
27+ */
28+export function AddAgent(form: $models.AgentForm): $CancellablePromise<string[] | null> {
29+ return $Call.ByID(698361705, form);
30+}
31+
19 /**32 /**
20 * Choose asks the user for a configuration file and opens it. The dialog33 * Choose asks the user for a configuration file and opens it. The dialog
21 * starts in the folder of the configuration in use: on macOS the default one34 * starts in the folder of the configuration in use: on macOS the default one
@@ -26,6 +39,15 @@ export function Choose(): $CancellablePromise<string> {
26 return $Call.ByID(1890400804);39 return $Call.ByID(1890400804);
27 }40 }
28 41
42+/**
43+ * ChooseFolder asks the user for a folder, starting in their home folder,
44+ * for the assistant's "agent's folder" field. It returns "" when the user
45+ * cancelled.
46+ */
47+export function ChooseFolder(): $CancellablePromise<string> {
48+ return $Call.ByID(1715751888);
49+}
50+
29 /**51 /**
30 * Content returns the text of the configuration file, for the editor.52 * Content returns the text of the configuration file, for the editor.
31 */53 */
modified frontend/bindings/git.rickub.com/bots-garden/sidekicks/internal/app/index.ts +1 -0
@@ -15,6 +15,7 @@ export {
1515 };
1616
1717 export type {
18+ AgentForm,
1819 ConfigView,
1920 OlderView,
2021 ProblemView,
@@ -15,6 +15,7 @@ export {
15 };15 };
16 16
17 export type {17 export type {
18+ AgentForm,
18 ConfigView,19 ConfigView,
19 OlderView,20 OlderView,
20 ProblemView,21 ProblemView,
modified frontend/bindings/git.rickub.com/bots-garden/sidekicks/internal/app/models.ts +37 -0
@@ -8,6 +8,43 @@ import * as channels$0 from "../channels/models.js";
88 // @ts-ignore: Unused imports
99 import * as workspace$0 from "../workspace/models.js";
1010
11+/**
12+ * AgentForm is what the "add an agent" assistant asks for. Sidekicks turns
13+ * it into the agent's entry of the configuration file (see entryOf), so the
14+ * user never writes YAML.
15+ */
16+export interface AgentForm {
17+ "name": string;
18+ "title": string;
19+ "icon": string;
20+ "description": string;
21+
22+ /**
23+ * Environment is local, container or sbx.
24+ */
25+ "environment": string;
26+
27+ /**
28+ * Box is the name of the Docker container or of the sbx sandbox.
29+ */
30+ "box": string;
31+
32+ /**
33+ * Program is the agent's program, such as mm.
34+ */
35+ "program": string;
36+
37+ /**
38+ * Arguments are typed on one line, as in a terminal: quotes group words.
39+ */
40+ "arguments": string;
41+
42+ /**
43+ * Workspace is the agent's folder on this computer. It is created if needed.
44+ */
45+ "workspace": string;
46+}
47+
1148 /**
1249 * ConfigView is the configuration as the interface shows it.
1350 */
@@ -8,6 +8,43 @@ import * as channels$0 from "../channels/models.js";
8 // @ts-ignore: Unused imports8 // @ts-ignore: Unused imports
9 import * as workspace$0 from "../workspace/models.js";9 import * as workspace$0 from "../workspace/models.js";
10 10
11+/**
12+ * AgentForm is what the "add an agent" assistant asks for. Sidekicks turns
13+ * it into the agent's entry of the configuration file (see entryOf), so the
14+ * user never writes YAML.
15+ */
16+export interface AgentForm {
17+ "name": string;
18+ "title": string;
19+ "icon": string;
20+ "description": string;
21+
22+ /**
23+ * Environment is local, container or sbx.
24+ */
25+ "environment": string;
26+
27+ /**
28+ * Box is the name of the Docker container or of the sbx sandbox.
29+ */
30+ "box": string;
31+
32+ /**
33+ * Program is the agent's program, such as mm.
34+ */
35+ "program": string;
36+
37+ /**
38+ * Arguments are typed on one line, as in a terminal: quotes group words.
39+ */
40+ "arguments": string;
41+
42+ /**
43+ * Workspace is the agent's folder on this computer. It is created if needed.
44+ */
45+ "workspace": string;
46+}
47+
11 /**48 /**
12 * ConfigView is the configuration as the interface shows it.49 * ConfigView is the configuration as the interface shows it.
13 */50 */
added frontend/src/lib/agent/AddAgentForm.svelte +165 -0
new file mode 100644
@@ -0,0 +1,165 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The "add an agent" assistant (issue #33, wireframe 6): a form instead of
4+ // YAML. ConfigService.AddAgent writes the entry, keeping the file's
5+ // comments; the agent's direct conversation opens once it is loaded.
6+ import { t, type MessageKey } from '../i18n/index.svelte'
7+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
8+ import { emptyForm, nameFromTitle, problemsOf, type Environment, type Missing } from '../agentform'
9+ import { agentAdded, appState } from '../state.svelte'
10+
11+ const form = $state(emptyForm())
12+ // The name follows the title until the user edits it.
13+ let nameEdited = $state(false)
14+ let tried = $state(false)
15+ let adding = $state(false)
16+ let refused = $state<string[]>([])
17+ let failure = $state('')
18+
19+ const environments: Environment[] = ['local', 'container', 'sbx']
20+ const missing = $derived(problemsOf(form))
21+ const shown = $derived(tried ? missing : [])
22+
23+ function onTitle() {
24+ if (!nameEdited) form.name = nameFromTitle(form.title)
25+ }
26+
27+ async function choose() {
28+ try {
29+ const folder = await ConfigService.ChooseFolder()
30+ if (folder) form.workspace = folder
31+ } catch (error) {
32+ failure = error instanceof Error ? error.message : String(error)
33+ }
34+ }
35+
36+ async function submit(event: SubmitEvent) {
37+ event.preventDefault()
38+ tried = true
39+ refused = []
40+ failure = ''
41+ if (missing.length > 0) return
42+ adding = true
43+ try {
44+ const problems = (await ConfigService.AddAgent({ ...form, title: form.title.trim() || form.name })) ?? []
45+ if (problems.length > 0) refused = problems
46+ else agentAdded(form.name.trim())
47+ } catch (error) {
48+ failure = error instanceof Error ? error.message : String(error)
49+ } finally {
50+ adding = false
51+ }
52+ }
53+
54+ function has(problem: Missing): boolean {
55+ return shown.includes(problem)
56+ }
57+</script>
58+
59+<form class="assistant" onsubmit={submit} novalidate>
60+ <h1>{t('assistant.title')}</h1>
61+ <p class="intro">{t('assistant.intro')}</p>
62+
63+ <fieldset>
64+ <legend>{t('assistant.who')}</legend>
65+ <div class="row">
66+ <label class="icon">
67+ {t('assistant.icon')}
68+ <input bind:value={form.icon} maxlength="8" />
69+ </label>
70+ <label class="grow">
71+ {t('assistant.agentTitle')}
72+ <!-- svelte-ignore a11y_autofocus -->
73+ <input bind:value={form.title} oninput={onTitle} placeholder={t('assistant.agentTitlePlaceholder')} autofocus />
74+ </label>
75+ </div>
76+ <label>
77+ {t('assistant.name')}
78+ <input bind:value={form.name} oninput={() => (nameEdited = true)} placeholder="alice" aria-invalid={has('name') || has('nameFormat')} aria-describedby="name-hint" spellcheck="false" autocapitalize="off" />
79+ <small id="name-hint" class:problem={has('name') || has('nameFormat')}>
80+ {has('name') ? t('assistant.missing.name') : has('nameFormat') ? t('assistant.missing.nameFormat') : t('assistant.nameHint')}
81+ </small>
82+ </label>
83+ <label>
84+ {t('assistant.description')}
85+ <input bind:value={form.description} placeholder={t('assistant.descriptionPlaceholder')} />
86+ </label>
87+ </fieldset>
88+
89+ <fieldset>
90+ <legend>{t('assistant.where')}</legend>
91+ <div class="choices" role="radiogroup">
92+ {#each environments as environment (environment)}
93+ <label class="choice" class:chosen={form.environment === environment}>
94+ <input type="radio" name="environment" value={environment} bind:group={form.environment} />
95+ {t(`assistant.${environment}` as MessageKey)}
96+ </label>
97+ {/each}
98+ </div>
99+ {#if form.environment !== 'local'}
100+ <label>
101+ {form.environment === 'container' ? t('assistant.containerName') : t('assistant.sandboxName')}
102+ <input bind:value={form.box} placeholder={form.environment === 'container' ? 'scout-box' : 'milo-sbx'} aria-invalid={has('box')} aria-describedby="box-hint" spellcheck="false" autocapitalize="off" />
103+ <small id="box-hint" class:problem={has('box')}>{has('box') ? t('assistant.missing.box') : t('assistant.boxHint')}</small>
104+ </label>
105+ {/if}
106+ </fieldset>
107+
108+ <fieldset>
109+ <legend>{t('assistant.how')}</legend>
110+ <label>
111+ {t('assistant.program')}
112+ <input bind:value={form.program} placeholder={t('assistant.programPlaceholder')} aria-invalid={has('program')} aria-describedby="program-hint" spellcheck="false" autocapitalize="off" />
113+ <small id="program-hint" class:problem={has('program')}>{has('program') ? t('assistant.missing.program') : t('assistant.programHint')}</small>
114+ </label>
115+ <label>
116+ {t('assistant.arguments')}
117+ <input bind:value={form.arguments} placeholder={t('assistant.argumentsPlaceholder')} spellcheck="false" autocapitalize="off" />
118+ </label>
119+ <label for="workspace">{t('assistant.workspace')}</label>
120+ <div class="row centered">
121+ <input id="workspace" class="grow" bind:value={form.workspace} placeholder="~/agents/alice" aria-invalid={has('workspace')} aria-describedby="workspace-hint" spellcheck="false" autocapitalize="off" />
122+ <button type="button" onclick={choose}>{t('assistant.choose')}</button>
123+ </div>
124+ <small id="workspace-hint" class:problem={has('workspace')}>{has('workspace') ? t('assistant.missing.workspace') : t('assistant.workspaceHint')}</small>
125+ </fieldset>
126+
127+ {#if refused.length > 0 || failure}
128+ <div class="refused" role="alert">
129+ <strong>{t('assistant.refused')}</strong>
130+ <ul>
131+ {#each refused as problem, index (index)}<li>{problem}</li>{/each}
132+ {#if failure}<li>{failure}</li>{/if}
133+ </ul>
134+ </div>
135+ {/if}
136+
137+ <div class="actions">
138+ <button type="button" onclick={() => (appState.assistant = false)}>{t('assistant.cancel')}</button>
139+ <button type="submit" class="primary" disabled={adding}>{adding ? t('assistant.adding') : t('assistant.add')}</button>
140+ </div>
141+</form>
142+
143+<style>
144+ .assistant { max-width: 620px; margin: 0 auto; padding: 24px 24px 32px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
145+ h1 { font-size: 20px; margin: 0; }
146+ .intro { color: var(--muted); margin: 0; }
147+ fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px 14px; display: flex; flex-direction: column; gap: 10px; background: var(--panel); }
148+ legend { font-weight: 600; padding: 0 4px; }
149+ label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
150+ input:not([type='radio']) { font: inherit; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
151+ input[aria-invalid='true'] { border-color: var(--danger); }
152+ small { color: var(--muted); font-size: 12px; }
153+ small.problem { color: var(--danger); }
154+ .row { display: flex; gap: 8px; align-items: flex-end; }
155+ .row.centered { align-items: center; }
156+ .grow { flex: 1; min-width: 0; }
157+ .icon input { width: 56px; text-align: center; }
158+ .choices { display: flex; flex-wrap: wrap; gap: 8px; }
159+ .choice { flex-direction: row; align-items: center; gap: 6px; border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px; cursor: pointer; }
160+ .choice.chosen { border-color: var(--accent); }
161+ .refused { border: 1px solid var(--danger); border-radius: var(--radius); color: var(--danger); padding: 8px 12px; font-size: 13px; }
162+ .refused ul { margin: 4px 0 0; padding-left: 18px; }
163+ .actions { display: flex; justify-content: flex-end; gap: 8px; }
164+ .primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
165+</style>
new file mode 100644
@@ -0,0 +1,165 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The "add an agent" assistant (issue #33, wireframe 6): a form instead of
4+ // YAML. ConfigService.AddAgent writes the entry, keeping the file's
5+ // comments; the agent's direct conversation opens once it is loaded.
6+ import { t, type MessageKey } from '../i18n/index.svelte'
7+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
8+ import { emptyForm, nameFromTitle, problemsOf, type Environment, type Missing } from '../agentform'
9+ import { agentAdded, appState } from '../state.svelte'
10+
11+ const form = $state(emptyForm())
12+ // The name follows the title until the user edits it.
13+ let nameEdited = $state(false)
14+ let tried = $state(false)
15+ let adding = $state(false)
16+ let refused = $state<string[]>([])
17+ let failure = $state('')
18+
19+ const environments: Environment[] = ['local', 'container', 'sbx']
20+ const missing = $derived(problemsOf(form))
21+ const shown = $derived(tried ? missing : [])
22+
23+ function onTitle() {
24+ if (!nameEdited) form.name = nameFromTitle(form.title)
25+ }
26+
27+ async function choose() {
28+ try {
29+ const folder = await ConfigService.ChooseFolder()
30+ if (folder) form.workspace = folder
31+ } catch (error) {
32+ failure = error instanceof Error ? error.message : String(error)
33+ }
34+ }
35+
36+ async function submit(event: SubmitEvent) {
37+ event.preventDefault()
38+ tried = true
39+ refused = []
40+ failure = ''
41+ if (missing.length > 0) return
42+ adding = true
43+ try {
44+ const problems = (await ConfigService.AddAgent({ ...form, title: form.title.trim() || form.name })) ?? []
45+ if (problems.length > 0) refused = problems
46+ else agentAdded(form.name.trim())
47+ } catch (error) {
48+ failure = error instanceof Error ? error.message : String(error)
49+ } finally {
50+ adding = false
51+ }
52+ }
53+
54+ function has(problem: Missing): boolean {
55+ return shown.includes(problem)
56+ }
57+</script>
58+
59+<form class="assistant" onsubmit={submit} novalidate>
60+ <h1>{t('assistant.title')}</h1>
61+ <p class="intro">{t('assistant.intro')}</p>
62+
63+ <fieldset>
64+ <legend>{t('assistant.who')}</legend>
65+ <div class="row">
66+ <label class="icon">
67+ {t('assistant.icon')}
68+ <input bind:value={form.icon} maxlength="8" />
69+ </label>
70+ <label class="grow">
71+ {t('assistant.agentTitle')}
72+ <!-- svelte-ignore a11y_autofocus -->
73+ <input bind:value={form.title} oninput={onTitle} placeholder={t('assistant.agentTitlePlaceholder')} autofocus />
74+ </label>
75+ </div>
76+ <label>
77+ {t('assistant.name')}
78+ <input bind:value={form.name} oninput={() => (nameEdited = true)} placeholder="alice" aria-invalid={has('name') || has('nameFormat')} aria-describedby="name-hint" spellcheck="false" autocapitalize="off" />
79+ <small id="name-hint" class:problem={has('name') || has('nameFormat')}>
80+ {has('name') ? t('assistant.missing.name') : has('nameFormat') ? t('assistant.missing.nameFormat') : t('assistant.nameHint')}
81+ </small>
82+ </label>
83+ <label>
84+ {t('assistant.description')}
85+ <input bind:value={form.description} placeholder={t('assistant.descriptionPlaceholder')} />
86+ </label>
87+ </fieldset>
88+
89+ <fieldset>
90+ <legend>{t('assistant.where')}</legend>
91+ <div class="choices" role="radiogroup">
92+ {#each environments as environment (environment)}
93+ <label class="choice" class:chosen={form.environment === environment}>
94+ <input type="radio" name="environment" value={environment} bind:group={form.environment} />
95+ {t(`assistant.${environment}` as MessageKey)}
96+ </label>
97+ {/each}
98+ </div>
99+ {#if form.environment !== 'local'}
100+ <label>
101+ {form.environment === 'container' ? t('assistant.containerName') : t('assistant.sandboxName')}
102+ <input bind:value={form.box} placeholder={form.environment === 'container' ? 'scout-box' : 'milo-sbx'} aria-invalid={has('box')} aria-describedby="box-hint" spellcheck="false" autocapitalize="off" />
103+ <small id="box-hint" class:problem={has('box')}>{has('box') ? t('assistant.missing.box') : t('assistant.boxHint')}</small>
104+ </label>
105+ {/if}
106+ </fieldset>
107+
108+ <fieldset>
109+ <legend>{t('assistant.how')}</legend>
110+ <label>
111+ {t('assistant.program')}
112+ <input bind:value={form.program} placeholder={t('assistant.programPlaceholder')} aria-invalid={has('program')} aria-describedby="program-hint" spellcheck="false" autocapitalize="off" />
113+ <small id="program-hint" class:problem={has('program')}>{has('program') ? t('assistant.missing.program') : t('assistant.programHint')}</small>
114+ </label>
115+ <label>
116+ {t('assistant.arguments')}
117+ <input bind:value={form.arguments} placeholder={t('assistant.argumentsPlaceholder')} spellcheck="false" autocapitalize="off" />
118+ </label>
119+ <label for="workspace">{t('assistant.workspace')}</label>
120+ <div class="row centered">
121+ <input id="workspace" class="grow" bind:value={form.workspace} placeholder="~/agents/alice" aria-invalid={has('workspace')} aria-describedby="workspace-hint" spellcheck="false" autocapitalize="off" />
122+ <button type="button" onclick={choose}>{t('assistant.choose')}</button>
123+ </div>
124+ <small id="workspace-hint" class:problem={has('workspace')}>{has('workspace') ? t('assistant.missing.workspace') : t('assistant.workspaceHint')}</small>
125+ </fieldset>
126+
127+ {#if refused.length > 0 || failure}
128+ <div class="refused" role="alert">
129+ <strong>{t('assistant.refused')}</strong>
130+ <ul>
131+ {#each refused as problem, index (index)}<li>{problem}</li>{/each}
132+ {#if failure}<li>{failure}</li>{/if}
133+ </ul>
134+ </div>
135+ {/if}
136+
137+ <div class="actions">
138+ <button type="button" onclick={() => (appState.assistant = false)}>{t('assistant.cancel')}</button>
139+ <button type="submit" class="primary" disabled={adding}>{adding ? t('assistant.adding') : t('assistant.add')}</button>
140+ </div>
141+</form>
142+
143+<style>
144+ .assistant { max-width: 620px; margin: 0 auto; padding: 24px 24px 32px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
145+ h1 { font-size: 20px; margin: 0; }
146+ .intro { color: var(--muted); margin: 0; }
147+ fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px 14px; display: flex; flex-direction: column; gap: 10px; background: var(--panel); }
148+ legend { font-weight: 600; padding: 0 4px; }
149+ label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
150+ input:not([type='radio']) { font: inherit; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
151+ input[aria-invalid='true'] { border-color: var(--danger); }
152+ small { color: var(--muted); font-size: 12px; }
153+ small.problem { color: var(--danger); }
154+ .row { display: flex; gap: 8px; align-items: flex-end; }
155+ .row.centered { align-items: center; }
156+ .grow { flex: 1; min-width: 0; }
157+ .icon input { width: 56px; text-align: center; }
158+ .choices { display: flex; flex-wrap: wrap; gap: 8px; }
159+ .choice { flex-direction: row; align-items: center; gap: 6px; border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px; cursor: pointer; }
160+ .choice.chosen { border-color: var(--accent); }
161+ .refused { border: 1px solid var(--danger); border-radius: var(--radius); color: var(--danger); padding: 8px 12px; font-size: 13px; }
162+ .refused ul { margin: 4px 0 0; padding-left: 18px; }
163+ .actions { display: flex; justify-content: flex-end; gap: 8px; }
164+ .primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
165+</style>
modified frontend/src/lib/agent/AgentView.svelte +2 -1
@@ -10,6 +10,7 @@
1010 import AgentLog from './AgentLog.svelte'
1111 import FilesTab from './FilesTab.svelte'
1212 import TerminalView from './TerminalView.svelte'
13+ import ErrorText from '../chat/ErrorText.svelte'
1314
1415 let { agent: name }: { agent: string } = $props()
1516
@@ -68,7 +69,7 @@
6869
6970 <div class="body">
7071 {#if problem}
71- <p class="error">{t('workspace.unavailable', { agent: agent.title, problem })}</p>
72+ <div class="error">{t('workspace.unavailable', { agent: agent.title })} <ErrorText raw={problem} /></div>
7273 {:else}
7374 <div class="pane" class:hidden={tab !== 'files'}>
7475 <FilesTab agent={name} inContainer={!location.startsWith('local')} />
@@ -10,6 +10,7 @@
10 import AgentLog from './AgentLog.svelte'10 import AgentLog from './AgentLog.svelte'
11 import FilesTab from './FilesTab.svelte'11 import FilesTab from './FilesTab.svelte'
12 import TerminalView from './TerminalView.svelte'12 import TerminalView from './TerminalView.svelte'
13+ import ErrorText from '../chat/ErrorText.svelte'
13 14
14 let { agent: name }: { agent: string } = $props()15 let { agent: name }: { agent: string } = $props()
15 16
@@ -68,7 +69,7 @@
68 69
69 <div class="body">70 <div class="body">
70 {#if problem}71 {#if problem}
71- <p class="error">{t('workspace.unavailable', { agent: agent.title, problem })}</p>72+ <div class="error">{t('workspace.unavailable', { agent: agent.title })} <ErrorText raw={problem} /></div>
72 {:else}73 {:else}
73 <div class="pane" class:hidden={tab !== 'files'}>74 <div class="pane" class:hidden={tab !== 'files'}>
74 <FilesTab agent={name} inContainer={!location.startsWith('local')} />75 <FilesTab agent={name} inContainer={!location.startsWith('local')} />
modified frontend/src/lib/agent/TerminalView.svelte +2 -1
@@ -10,6 +10,7 @@
1010 import { TerminalService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
1111 import { t } from '../i18n/index.svelte'
1212 import { listenTerminal } from '../terminals'
13+ import ErrorText from '../chat/ErrorText.svelte'
1314
1415 let { agent, visible }: { agent: string; visible: boolean } = $props()
1516
@@ -87,7 +88,7 @@
8788 <div class="screen" bind:this={host}></div>
8889 {#if ended || error}
8990 <div class="ended">
90- <span>{error ? t('terminal.error', { error }) : t('terminal.ended')}</span>
91+ <span>{#if error}{t('terminal.error')} <ErrorText raw={error} />{:else}{t('terminal.ended')}{/if}</span>
9192 <button onclick={openShell}>{t('terminal.restart')}</button>
9293 </div>
9394 {/if}
@@ -10,6 +10,7 @@
10 import { TerminalService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'10 import { TerminalService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
11 import { t } from '../i18n/index.svelte'11 import { t } from '../i18n/index.svelte'
12 import { listenTerminal } from '../terminals'12 import { listenTerminal } from '../terminals'
13+ import ErrorText from '../chat/ErrorText.svelte'
13 14
14 let { agent, visible }: { agent: string; visible: boolean } = $props()15 let { agent, visible }: { agent: string; visible: boolean } = $props()
15 16
@@ -87,7 +88,7 @@
87 <div class="screen" bind:this={host}></div>88 <div class="screen" bind:this={host}></div>
88 {#if ended || error}89 {#if ended || error}
89 <div class="ended">90 <div class="ended">
90- <span>{error ? t('terminal.error', { error }) : t('terminal.ended')}</span>91+ <span>{#if error}{t('terminal.error')} <ErrorText raw={error} />{:else}{t('terminal.ended')}{/if}</span>
91 <button onclick={openShell}>{t('terminal.restart')}</button>92 <button onclick={openShell}>{t('terminal.restart')}</button>
92 </div>93 </div>
93 {/if}94 {/if}
added frontend/src/lib/agentform.test.ts +33 -0
new file mode 100644
@@ -0,0 +1,33 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { emptyForm, nameFromTitle, problemsOf } from './agentform'
5+
6+describe('nameFromTitle', () => {
7+ it('folds accents, lowercases and joins words', () => {
8+ expect(nameFromTitle('Hélène (traductrice)')).toBe('helene-traductrice')
9+ expect(nameFromTitle('Bob')).toBe('bob')
10+ expect(nameFromTitle('code_reviewer 2')).toBe('code_reviewer-2')
11+ })
12+ it('starts with a letter and stays within 32 characters', () => {
13+ expect(nameFromTitle('42 Bob')).toBe('bob')
14+ expect(nameFromTitle('🐮 Garfield')).toBe('garfield')
15+ expect(nameFromTitle('a'.repeat(40))).toHaveLength(32)
16+ expect(nameFromTitle('')).toBe('')
17+ })
18+})
19+
20+describe('problemsOf', () => {
21+ it('lists the missing fields of a local agent', () => {
22+ expect(problemsOf({ ...emptyForm(), title: 'Alice' })).toEqual(['name', 'program', 'workspace'])
23+ expect(problemsOf({ ...emptyForm(), name: 'alice', program: 'mm', workspace: '/agents/alice' })).toEqual([])
24+ })
25+ it('checks the name like the configuration does', () => {
26+ expect(problemsOf({ ...emptyForm(), name: 'Alice B', program: 'mm', workspace: '/a' })).toEqual(['nameFormat'])
27+ })
28+ it('asks for the container or sandbox name outside this computer', () => {
29+ const form = { ...emptyForm(), name: 'scout', program: 'mm', workspace: '/a', environment: 'container' as const }
30+ expect(problemsOf(form)).toEqual(['box'])
31+ expect(problemsOf({ ...form, environment: 'sbx', box: 'milo-sbx' })).toEqual([])
32+ })
33+})
new file mode 100644
@@ -0,0 +1,33 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { emptyForm, nameFromTitle, problemsOf } from './agentform'
5+
6+describe('nameFromTitle', () => {
7+ it('folds accents, lowercases and joins words', () => {
8+ expect(nameFromTitle('Hélène (traductrice)')).toBe('helene-traductrice')
9+ expect(nameFromTitle('Bob')).toBe('bob')
10+ expect(nameFromTitle('code_reviewer 2')).toBe('code_reviewer-2')
11+ })
12+ it('starts with a letter and stays within 32 characters', () => {
13+ expect(nameFromTitle('42 Bob')).toBe('bob')
14+ expect(nameFromTitle('🐮 Garfield')).toBe('garfield')
15+ expect(nameFromTitle('a'.repeat(40))).toHaveLength(32)
16+ expect(nameFromTitle('')).toBe('')
17+ })
18+})
19+
20+describe('problemsOf', () => {
21+ it('lists the missing fields of a local agent', () => {
22+ expect(problemsOf({ ...emptyForm(), title: 'Alice' })).toEqual(['name', 'program', 'workspace'])
23+ expect(problemsOf({ ...emptyForm(), name: 'alice', program: 'mm', workspace: '/agents/alice' })).toEqual([])
24+ })
25+ it('checks the name like the configuration does', () => {
26+ expect(problemsOf({ ...emptyForm(), name: 'Alice B', program: 'mm', workspace: '/a' })).toEqual(['nameFormat'])
27+ })
28+ it('asks for the container or sandbox name outside this computer', () => {
29+ const form = { ...emptyForm(), name: 'scout', program: 'mm', workspace: '/a', environment: 'container' as const }
30+ expect(problemsOf(form)).toEqual(['box'])
31+ expect(problemsOf({ ...form, environment: 'sbx', box: 'milo-sbx' })).toEqual([])
32+ })
33+})
added frontend/src/lib/agentform.ts +64 -0
new file mode 100644
@@ -0,0 +1,64 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// The "add an agent" assistant (issue #33): what it asks for, and what it
4+// checks before sending the form to ConfigService.AddAgent, which writes the
5+// YAML for the user.
6+
7+export type Environment = 'local' | 'container' | 'sbx'
8+
9+export interface AgentFormValues {
10+ name: string
11+ title: string
12+ icon: string
13+ description: string
14+ environment: Environment
15+ /** The container or sandbox name; unused for a local agent. */
16+ box: string
17+ program: string
18+ arguments: string
19+ workspace: string
20+}
21+
22+export function emptyForm(): AgentFormValues {
23+ return { name: '', title: '', icon: '🤖', description: '', environment: 'local', box: '', program: '', arguments: '', workspace: '' }
24+}
25+
26+/** The pattern of an agent name in the configuration schema. */
27+export const namePattern = /^[a-z][a-z0-9_-]{0,31}$/
28+
29+/**
30+ * The name used with @ derived from the title the user types: accents
31+ * folded, lowercase, words joined by "-", starting with a letter.
32+ *
33+ * @example
34+ * nameFromTitle('Hélène (traductrice)') // 'helene-traductrice'
35+ * nameFromTitle('42 Bob') // 'bob'
36+ */
37+export function nameFromTitle(title: string): string {
38+ const folded = title.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()
39+ return folded
40+ .replace(/[^a-z0-9_-]+/g, '-')
41+ .replace(/^[^a-z]+/, '')
42+ .slice(0, 32)
43+ .replace(/-+$/, '')
44+}
45+
46+/** The fields the user still has to fill in, by name. */
47+export type Missing = 'name' | 'nameFormat' | 'program' | 'workspace' | 'box'
48+
49+/**
50+ * What is missing or wrong before the form can be sent.
51+ *
52+ * @example
53+ * problemsOf({ ...emptyForm(), title: 'Alice' }) // ['name', 'program', 'workspace']
54+ */
55+export function problemsOf(form: AgentFormValues): Missing[] {
56+ const problems: Missing[] = []
57+ const name = form.name.trim()
58+ if (!name) problems.push('name')
59+ else if (!namePattern.test(name)) problems.push('nameFormat')
60+ if (!form.program.trim()) problems.push('program')
61+ if (!form.workspace.trim()) problems.push('workspace')
62+ if (form.environment !== 'local' && !form.box.trim()) problems.push('box')
63+ return problems
64+}
new file mode 100644
@@ -0,0 +1,64 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// The "add an agent" assistant (issue #33): what it asks for, and what it
4+// checks before sending the form to ConfigService.AddAgent, which writes the
5+// YAML for the user.
6+
7+export type Environment = 'local' | 'container' | 'sbx'
8+
9+export interface AgentFormValues {
10+ name: string
11+ title: string
12+ icon: string
13+ description: string
14+ environment: Environment
15+ /** The container or sandbox name; unused for a local agent. */
16+ box: string
17+ program: string
18+ arguments: string
19+ workspace: string
20+}
21+
22+export function emptyForm(): AgentFormValues {
23+ return { name: '', title: '', icon: '🤖', description: '', environment: 'local', box: '', program: '', arguments: '', workspace: '' }
24+}
25+
26+/** The pattern of an agent name in the configuration schema. */
27+export const namePattern = /^[a-z][a-z0-9_-]{0,31}$/
28+
29+/**
30+ * The name used with @ derived from the title the user types: accents
31+ * folded, lowercase, words joined by "-", starting with a letter.
32+ *
33+ * @example
34+ * nameFromTitle('Hélène (traductrice)') // 'helene-traductrice'
35+ * nameFromTitle('42 Bob') // 'bob'
36+ */
37+export function nameFromTitle(title: string): string {
38+ const folded = title.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()
39+ return folded
40+ .replace(/[^a-z0-9_-]+/g, '-')
41+ .replace(/^[^a-z]+/, '')
42+ .slice(0, 32)
43+ .replace(/-+$/, '')
44+}
45+
46+/** The fields the user still has to fill in, by name. */
47+export type Missing = 'name' | 'nameFormat' | 'program' | 'workspace' | 'box'
48+
49+/**
50+ * What is missing or wrong before the form can be sent.
51+ *
52+ * @example
53+ * problemsOf({ ...emptyForm(), title: 'Alice' }) // ['name', 'program', 'workspace']
54+ */
55+export function problemsOf(form: AgentFormValues): Missing[] {
56+ const problems: Missing[] = []
57+ const name = form.name.trim()
58+ if (!name) problems.push('name')
59+ else if (!namePattern.test(name)) problems.push('nameFormat')
60+ if (!form.program.trim()) problems.push('program')
61+ if (!form.workspace.trim()) problems.push('workspace')
62+ if (form.environment !== 'local' && !form.box.trim()) problems.push('box')
63+ return problems
64+}
modified frontend/src/lib/chat/ActionError.svelte +7 -3
@@ -4,17 +4,21 @@
44 // channel…), until the user closes it or the configuration changes.
55 import { t } from '../i18n/index.svelte'
66 import { appState } from '../state.svelte'
7+ import ErrorText from './ErrorText.svelte'
8+
9+ // An agent that failed to start already shows the same error on its row.
10+ const shown = $derived(appState.actionError !== '' && !appState.agents.some((agent) => !!agent.error && appState.actionError.includes(agent.error)))
711 </script>
812
9-{#if appState.actionError}
13+{#if shown}
1014 <p class="error" role="alert">
11- <span>{appState.actionError}</span>
15+ <span class="text"><ErrorText raw={appState.actionError} /></span>
1216 <button onclick={() => (appState.actionError = '')} aria-label={t('error.dismiss')} title={t('error.dismiss')}>×</button>
1317 </p>
1418 {/if}
1519
1620 <style>
1721 .error { display: flex; gap: 6px; align-items: flex-start; margin: 6px 8px; color: var(--danger); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
18- span { flex: 1; }
22+ .text { flex: 1; }
1923 button { border: none; background: none; color: var(--danger); padding: 0 4px; font-size: 14px; line-height: 1; }
2024 </style>
@@ -4,17 +4,21 @@
4 // channel…), until the user closes it or the configuration changes.4 // channel…), until the user closes it or the configuration changes.
5 import { t } from '../i18n/index.svelte'5 import { t } from '../i18n/index.svelte'
6 import { appState } from '../state.svelte'6 import { appState } from '../state.svelte'
7+ import ErrorText from './ErrorText.svelte'
8+
9+ // An agent that failed to start already shows the same error on its row.
10+ const shown = $derived(appState.actionError !== '' && !appState.agents.some((agent) => !!agent.error && appState.actionError.includes(agent.error)))
7 </script>11 </script>
8 12
9-{#if appState.actionError}13+{#if shown}
10 <p class="error" role="alert">14 <p class="error" role="alert">
11- <span>{appState.actionError}</span>15+ <span class="text"><ErrorText raw={appState.actionError} /></span>
12 <button onclick={() => (appState.actionError = '')} aria-label={t('error.dismiss')} title={t('error.dismiss')}>×</button>16 <button onclick={() => (appState.actionError = '')} aria-label={t('error.dismiss')} title={t('error.dismiss')}>×</button>
13 </p>17 </p>
14 {/if}18 {/if}
15 19
16 <style>20 <style>
17 .error { display: flex; gap: 6px; align-items: flex-start; margin: 6px 8px; color: var(--danger); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }21 .error { display: flex; gap: 6px; align-items: flex-start; margin: 6px 8px; color: var(--danger); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
18- span { flex: 1; }22+ .text { flex: 1; }
19 button { border: none; background: none; color: var(--danger); padding: 0 4px; font-size: 14px; line-height: 1; }23 button { border: none; background: none; color: var(--danger); padding: 0 4px; font-size: 14px; line-height: 1; }
20 </style>24 </style>
added frontend/src/lib/chat/ErrorText.svelte +27 -0
new file mode 100644
@@ -0,0 +1,27 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // An error in plain words when it is a known one (lib/errors.ts), with the
4+ // technical text folded below; otherwise the text as it is.
5+ // Usage: <ErrorText raw={agent.error} />
6+ import { explain } from '../errors'
7+ import { t } from '../i18n/index.svelte'
8+
9+ let { raw }: { raw: string } = $props()
10+ const explanation = $derived(explain(raw))
11+</script>
12+
13+{#if explanation}
14+ <span class="explained">{t(explanation.key, explanation.vars)}</span>
15+ <details>
16+ <summary>{t('error.details')}</summary>
17+ <code>{raw}</code>
18+ </details>
19+{:else}
20+ <span>{raw}</span>
21+{/if}
22+
23+<style>
24+ details { margin-top: 2px; font-size: 11px; opacity: 0.85; }
25+ summary { cursor: pointer; }
26+ code { display: block; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--mono); }
27+</style>
new file mode 100644
@@ -0,0 +1,27 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // An error in plain words when it is a known one (lib/errors.ts), with the
4+ // technical text folded below; otherwise the text as it is.
5+ // Usage: <ErrorText raw={agent.error} />
6+ import { explain } from '../errors'
7+ import { t } from '../i18n/index.svelte'
8+
9+ let { raw }: { raw: string } = $props()
10+ const explanation = $derived(explain(raw))
11+</script>
12+
13+{#if explanation}
14+ <span class="explained">{t(explanation.key, explanation.vars)}</span>
15+ <details>
16+ <summary>{t('error.details')}</summary>
17+ <code>{raw}</code>
18+ </details>
19+{:else}
20+ <span>{raw}</span>
21+{/if}
22+
23+<style>
24+ details { margin-top: 2px; font-size: 11px; opacity: 0.85; }
25+ summary { cursor: pointer; }
26+ code { display: block; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--mono); }
27+</style>
modified frontend/src/lib/chat/MessageView.svelte +18 -3
@@ -4,10 +4,11 @@
44 import type { Message } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
55 import { agentTitle, appState } from '../state.svelte'
66 import { splitMentions } from '../suggestions'
7- import { activityOf } from '../conversation'
7+ import { activityOf, answerText } from '../conversation'
88 import Markdown from './Markdown.svelte'
99 import Spinner from './Spinner.svelte'
1010 import ToolCallCard from './ToolCallCard.svelte'
11+ import ErrorText from './ErrorText.svelte'
1112
1213 let { message }: { message: Message } = $props()
1314
@@ -21,17 +22,29 @@
2122 // Codes and stop reasons come from Go; their sentences live in i18n.
2223 const systemKey = $derived(`system.${message.code ?? ''}`)
2324 const stopKey = $derived(`message.stop.${message.stopReason ?? ''}`)
25+ // The whole answer, as Markdown, once it is complete.
26+ const copyable = $derived(message.role === 'agent' && message.done ? answerText(message) : '')
27+ let copied = $state(false)
28+ async function copyAnswer() {
29+ await navigator.clipboard.writeText(copyable)
30+ copied = true
31+ setTimeout(() => (copied = false), 1500)
32+ }
33+
2434 const planIcons: Record<string, string> = { completed: '✓', in_progress: '⏵', pending: '○' }
2535 </script>
2636
2737 {#if message.role === 'system'}
28- <p class="system">{hasMessage(systemKey) ? t(systemKey, { agent: author, example }) : author}{#if message.error}<br /><span class="detail">{message.error}</span>{/if}</p>
38+ <p class="system">{hasMessage(systemKey) ? t(systemKey, { agent: author, example }) : author}{#if message.error}<br /><span class="detail"><ErrorText raw={message.error} /></span>{/if}</p>
2939 {:else}
3040 <article class="message {message.role}">
3141 <div class="author">
3242 {message.role === 'user' ? t('message.you') : author}
3343 {#if message.queued}<span class="note">{t('message.queued')}</span>{/if}
3444 {#if activity}<span class="note activity"><Spinner /> {t(`message.${activity}`)}</span>{/if}
45+ {#if copyable}
46+ <button class="copy" onclick={copyAnswer} title={t('message.copy')} aria-label={t('message.copy')}>{copied ? t('code.copied') : `⧉ ${t('code.copy')}`}</button>
47+ {/if}
3548 </div>
3649 {#each message.parts ?? [] as part, index (index)}
3750 {#if part.kind === 'text' && message.role === 'user'}
@@ -54,7 +67,7 @@
5467 <p class="ending">{t(stopKey)}</p>
5568 {/if}
5669 {#if message.error && message.role === 'agent'}
57- <p class="error">{t('message.error', { error: message.error })}</p>
70+ <div class="error">{t('message.error')} <ErrorText raw={message.error} /></div>
5871 {:else if message.error === 'cancelled'}
5972 <p class="ending">{t('message.notSent')}</p>
6073 {/if}
@@ -78,4 +91,6 @@
7891 .plan .in_progress { font-weight: 600; }
7992 .ending { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
8093 .error { color: var(--danger); font-size: 13px; margin: 6px 0 0; }
94+ .copy { font-size: 11px; font-weight: 400; padding: 0 6px; color: var(--muted); background: none; opacity: 0; }
95+ .message:hover .copy, .copy:focus-visible { opacity: 1; }
8196 </style>
@@ -4,10 +4,11 @@
4 import type { Message } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'4 import type { Message } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
5 import { agentTitle, appState } from '../state.svelte'5 import { agentTitle, appState } from '../state.svelte'
6 import { splitMentions } from '../suggestions'6 import { splitMentions } from '../suggestions'
7- import { activityOf } from '../conversation'7+ import { activityOf, answerText } from '../conversation'
8 import Markdown from './Markdown.svelte'8 import Markdown from './Markdown.svelte'
9 import Spinner from './Spinner.svelte'9 import Spinner from './Spinner.svelte'
10 import ToolCallCard from './ToolCallCard.svelte'10 import ToolCallCard from './ToolCallCard.svelte'
11+ import ErrorText from './ErrorText.svelte'
11 12
12 let { message }: { message: Message } = $props()13 let { message }: { message: Message } = $props()
13 14
@@ -21,17 +22,29 @@
21 // Codes and stop reasons come from Go; their sentences live in i18n.22 // Codes and stop reasons come from Go; their sentences live in i18n.
22 const systemKey = $derived(`system.${message.code ?? ''}`)23 const systemKey = $derived(`system.${message.code ?? ''}`)
23 const stopKey = $derived(`message.stop.${message.stopReason ?? ''}`)24 const stopKey = $derived(`message.stop.${message.stopReason ?? ''}`)
25+ // The whole answer, as Markdown, once it is complete.
26+ const copyable = $derived(message.role === 'agent' && message.done ? answerText(message) : '')
27+ let copied = $state(false)
28+ async function copyAnswer() {
29+ await navigator.clipboard.writeText(copyable)
30+ copied = true
31+ setTimeout(() => (copied = false), 1500)
32+ }
33+
24 const planIcons: Record<string, string> = { completed: '✓', in_progress: '⏵', pending: '○' }34 const planIcons: Record<string, string> = { completed: '✓', in_progress: '⏵', pending: '○' }
25 </script>35 </script>
26 36
27 {#if message.role === 'system'}37 {#if message.role === 'system'}
28- <p class="system">{hasMessage(systemKey) ? t(systemKey, { agent: author, example }) : author}{#if message.error}<br /><span class="detail">{message.error}</span>{/if}</p>38+ <p class="system">{hasMessage(systemKey) ? t(systemKey, { agent: author, example }) : author}{#if message.error}<br /><span class="detail"><ErrorText raw={message.error} /></span>{/if}</p>
29 {:else}39 {:else}
30 <article class="message {message.role}">40 <article class="message {message.role}">
31 <div class="author">41 <div class="author">
32 {message.role === 'user' ? t('message.you') : author}42 {message.role === 'user' ? t('message.you') : author}
33 {#if message.queued}<span class="note">{t('message.queued')}</span>{/if}43 {#if message.queued}<span class="note">{t('message.queued')}</span>{/if}
34 {#if activity}<span class="note activity"><Spinner /> {t(`message.${activity}`)}</span>{/if}44 {#if activity}<span class="note activity"><Spinner /> {t(`message.${activity}`)}</span>{/if}
45+ {#if copyable}
46+ <button class="copy" onclick={copyAnswer} title={t('message.copy')} aria-label={t('message.copy')}>{copied ? t('code.copied') : `⧉ ${t('code.copy')}`}</button>
47+ {/if}
35 </div>48 </div>
36 {#each message.parts ?? [] as part, index (index)}49 {#each message.parts ?? [] as part, index (index)}
37 {#if part.kind === 'text' && message.role === 'user'}50 {#if part.kind === 'text' && message.role === 'user'}
@@ -54,7 +67,7 @@
54 <p class="ending">{t(stopKey)}</p>67 <p class="ending">{t(stopKey)}</p>
55 {/if}68 {/if}
56 {#if message.error && message.role === 'agent'}69 {#if message.error && message.role === 'agent'}
57- <p class="error">{t('message.error', { error: message.error })}</p>70+ <div class="error">{t('message.error')} <ErrorText raw={message.error} /></div>
58 {:else if message.error === 'cancelled'}71 {:else if message.error === 'cancelled'}
59 <p class="ending">{t('message.notSent')}</p>72 <p class="ending">{t('message.notSent')}</p>
60 {/if}73 {/if}
@@ -78,4 +91,6 @@
78 .plan .in_progress { font-weight: 600; }91 .plan .in_progress { font-weight: 600; }
79 .ending { color: var(--muted); font-size: 12px; margin: 6px 0 0; }92 .ending { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
80 .error { color: var(--danger); font-size: 13px; margin: 6px 0 0; }93 .error { color: var(--danger); font-size: 13px; margin: 6px 0 0; }
94+ .copy { font-size: 11px; font-weight: 400; padding: 0 6px; color: var(--muted); background: none; opacity: 0; }
95+ .message:hover .copy, .copy:focus-visible { opacity: 1; }
81 </style>96 </style>
added frontend/src/lib/contrast.test.ts +35 -0
new file mode 100644
@@ -0,0 +1,35 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { contrastRatio, tokenBlocks } from './contrast'
5+import css from '../style.css?raw'
6+
7+describe('contrastRatio', () => {
8+ it('goes from 1 to 21', () => {
9+ expect(contrastRatio('#000000', '#ffffff')).toBeCloseTo(21, 5)
10+ expect(contrastRatio('#777777', '#777777')).toBeCloseTo(1, 5)
11+ })
12+})
13+
14+// Every text colour of the theme on every background it is used on.
15+describe('the theme of style.css', () => {
16+ const [light, ...dark] = tokenBlocks(css)
17+ const themes = { light, dark: { ...light, ...dark[0] }, 'dark (forced)': { ...light, ...dark[1] } }
18+
19+ for (const [name, tokens] of Object.entries(themes)) {
20+ it(`${name}: text colours reach WCAG AA (4.5:1) on the backgrounds`, () => {
21+ const failures: string[] = []
22+ for (const text of ['text', 'muted', 'accent', 'danger', 'ok', 'warn']) {
23+ for (const background of ['bg', 'panel', 'panel-2']) {
24+ const ratio = contrastRatio(tokens[text], tokens[background])
25+ if (ratio < 4.5) failures.push(`--${text} on --${background}: ${ratio.toFixed(2)}`)
26+ }
27+ }
28+ expect(failures).toEqual([])
29+ })
30+ }
31+
32+ it('white text on the accent (primary buttons) reaches AA', () => {
33+ expect(contrastRatio('#ffffff', light.accent)).toBeGreaterThanOrEqual(4.5)
34+ })
35+})
new file mode 100644
@@ -0,0 +1,35 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { contrastRatio, tokenBlocks } from './contrast'
5+import css from '../style.css?raw'
6+
7+describe('contrastRatio', () => {
8+ it('goes from 1 to 21', () => {
9+ expect(contrastRatio('#000000', '#ffffff')).toBeCloseTo(21, 5)
10+ expect(contrastRatio('#777777', '#777777')).toBeCloseTo(1, 5)
11+ })
12+})
13+
14+// Every text colour of the theme on every background it is used on.
15+describe('the theme of style.css', () => {
16+ const [light, ...dark] = tokenBlocks(css)
17+ const themes = { light, dark: { ...light, ...dark[0] }, 'dark (forced)': { ...light, ...dark[1] } }
18+
19+ for (const [name, tokens] of Object.entries(themes)) {
20+ it(`${name}: text colours reach WCAG AA (4.5:1) on the backgrounds`, () => {
21+ const failures: string[] = []
22+ for (const text of ['text', 'muted', 'accent', 'danger', 'ok', 'warn']) {
23+ for (const background of ['bg', 'panel', 'panel-2']) {
24+ const ratio = contrastRatio(tokens[text], tokens[background])
25+ if (ratio < 4.5) failures.push(`--${text} on --${background}: ${ratio.toFixed(2)}`)
26+ }
27+ }
28+ expect(failures).toEqual([])
29+ })
30+ }
31+
32+ it('white text on the accent (primary buttons) reaches AA', () => {
33+ expect(contrastRatio('#ffffff', light.accent)).toBeGreaterThanOrEqual(4.5)
34+ })
35+})
added frontend/src/lib/contrast.ts +36 -0
new file mode 100644
@@ -0,0 +1,36 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// WCAG contrast (issue #35): the colours of the theme must stay readable.
4+// AA asks for 4.5:1 for text, 3:1 for large text and interface parts.
5+
6+/** The relative luminance of a #rrggbb colour. */
7+export function luminance(hex: string): number {
8+ const channels = [1, 3, 5].map((start) => parseInt(hex.slice(start, start + 2), 16) / 255)
9+ const [r, g, b] = channels.map((value) => (value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4))
10+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
11+}
12+
13+/**
14+ * The contrast ratio of two #rrggbb colours, from 1 to 21.
15+ *
16+ * @example
17+ * contrastRatio('#000000', '#ffffff') // 21
18+ */
19+export function contrastRatio(first: string, second: string): number {
20+ const [light, dark] = [luminance(first), luminance(second)].sort((a, b) => b - a)
21+ return (light + 0.05) / (dark + 0.05)
22+}
23+
24+/**
25+ * The colour tokens (--name: #rrggbb) of each block of a stylesheet, by
26+ * selector: ":root", then the dark blocks in their order.
27+ */
28+export function tokenBlocks(css: string): Record<string, string>[] {
29+ const blocks: Record<string, string>[] = []
30+ for (const match of css.matchAll(/\{([^{}]*)\}/g)) {
31+ const tokens: Record<string, string> = {}
32+ for (const token of match[1].matchAll(/--([\w-]+):\s*(#[0-9a-fA-F]{6})\s*;/g)) tokens[token[1]] = token[2].toLowerCase()
33+ if (Object.keys(tokens).length > 0) blocks.push(tokens)
34+ }
35+ return blocks
36+}
new file mode 100644
@@ -0,0 +1,36 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// WCAG contrast (issue #35): the colours of the theme must stay readable.
4+// AA asks for 4.5:1 for text, 3:1 for large text and interface parts.
5+
6+/** The relative luminance of a #rrggbb colour. */
7+export function luminance(hex: string): number {
8+ const channels = [1, 3, 5].map((start) => parseInt(hex.slice(start, start + 2), 16) / 255)
9+ const [r, g, b] = channels.map((value) => (value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4))
10+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
11+}
12+
13+/**
14+ * The contrast ratio of two #rrggbb colours, from 1 to 21.
15+ *
16+ * @example
17+ * contrastRatio('#000000', '#ffffff') // 21
18+ */
19+export function contrastRatio(first: string, second: string): number {
20+ const [light, dark] = [luminance(first), luminance(second)].sort((a, b) => b - a)
21+ return (light + 0.05) / (dark + 0.05)
22+}
23+
24+/**
25+ * The colour tokens (--name: #rrggbb) of each block of a stylesheet, by
26+ * selector: ":root", then the dark blocks in their order.
27+ */
28+export function tokenBlocks(css: string): Record<string, string>[] {
29+ const blocks: Record<string, string>[] = []
30+ for (const match of css.matchAll(/\{([^{}]*)\}/g)) {
31+ const tokens: Record<string, string> = {}
32+ for (const token of match[1].matchAll(/--([\w-]+):\s*(#[0-9a-fA-F]{6})\s*;/g)) tokens[token[1]] = token[2].toLowerCase()
33+ if (Object.keys(tokens).length > 0) blocks.push(tokens)
34+ }
35+ return blocks
36+}
modified frontend/src/lib/conversation.test.ts +17 -1
@@ -3,7 +3,7 @@
33 import { describe, expect, it } from 'vitest'
44 import type { Channel, Message } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
55 import { Role } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
6-import { activityOf, addPermission, visibleDirects, applyDelta, applyMessage, emptyConversation, prependMessages, removePermission, usagePercent } from './conversation'
6+import { activityOf, addPermission, answerText, visibleDirects, applyDelta, applyMessage, emptyConversation, prependMessages, removePermission, usagePercent } from './conversation'
77
88 const general: Channel = { id: 'c1', name: 'general', description: '', direct: '', members: ['alice'] }
99
@@ -86,3 +86,19 @@ describe('conversation events', () => {
8686 expect(visibleDirects(channels, ['alice', 'bob']).map((channel) => channel.direct)).toEqual(['alice'])
8787 })
8888 })
89+
90+describe('answerText', () => {
91+ it('keeps the text parts, as written, one paragraph apart', () => {
92+ const answer = { ...message('m1', true), parts: [
93+ { kind: 'thought', text: 'thinking…' },
94+ { kind: 'text', text: 'Here is the table:\n\n| a | b |\n' },
95+ { kind: 'tool', text: '' },
96+ { kind: 'text', text: ' Done. ' },
97+ ] } as Message
98+ expect(answerText(answer)).toBe('Here is the table:\n\n| a | b |\n\nDone.')
99+ })
100+ it('is empty when the answer has no text', () => {
101+ expect(answerText(message('m2', true))).toBe('')
102+ expect(answerText({ ...message('m3', true), parts: [{ kind: 'text', text: ' ' }] } as Message)).toBe('')
103+ })
104+})
@@ -3,7 +3,7 @@
3 import { describe, expect, it } from 'vitest'3 import { describe, expect, it } from 'vitest'
4 import type { Channel, Message } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'4 import type { Channel, Message } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
5 import { Role } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'5 import { Role } from '../../bindings/git.rickub.com/bots-garden/sidekicks/internal/channels'
6-import { activityOf, addPermission, visibleDirects, applyDelta, applyMessage, emptyConversation, prependMessages, removePermission, usagePercent } from './conversation'6+import { activityOf, addPermission, answerText, visibleDirects, applyDelta, applyMessage, emptyConversation, prependMessages, removePermission, usagePercent } from './conversation'
7 7
8 const general: Channel = { id: 'c1', name: 'general', description: '', direct: '', members: ['alice'] }8 const general: Channel = { id: 'c1', name: 'general', description: '', direct: '', members: ['alice'] }
9 9
@@ -86,3 +86,19 @@ describe('conversation events', () => {
86 expect(visibleDirects(channels, ['alice', 'bob']).map((channel) => channel.direct)).toEqual(['alice'])86 expect(visibleDirects(channels, ['alice', 'bob']).map((channel) => channel.direct)).toEqual(['alice'])
87 })87 })
88 })88 })
89+
90+describe('answerText', () => {
91+ it('keeps the text parts, as written, one paragraph apart', () => {
92+ const answer = { ...message('m1', true), parts: [
93+ { kind: 'thought', text: 'thinking…' },
94+ { kind: 'text', text: 'Here is the table:\n\n| a | b |\n' },
95+ { kind: 'tool', text: '' },
96+ { kind: 'text', text: ' Done. ' },
97+ ] } as Message
98+ expect(answerText(answer)).toBe('Here is the table:\n\n| a | b |\n\nDone.')
99+ })
100+ it('is empty when the answer has no text', () => {
101+ expect(answerText(message('m2', true))).toBe('')
102+ expect(answerText({ ...message('m3', true), parts: [{ kind: 'text', text: ' ' }] } as Message)).toBe('')
103+ })
104+})
modified frontend/src/lib/conversation.ts +16 -0
@@ -100,3 +100,19 @@ export function activityOf(message: Message): Activity | null {
100100 export function visibleDirects(channels: Channel[], agents: string[]): Channel[] {
101101 return channels.filter((channel) => channel.direct && agents.includes(channel.direct))
102102 }
103+
104+/**
105+ * The text of an answer, for the "copy the answer" button: its text parts,
106+ * as the agent wrote them (Markdown), one paragraph apart. Thoughts, tool
107+ * calls and plans are left out.
108+ *
109+ * @example
110+ * answerText(message) // "Here is the table:\n\n| name | role |…"
111+ */
112+export function answerText(message: Message): string {
113+ return (message.parts ?? [])
114+ .filter((part) => part.kind === 'text')
115+ .map((part) => (part.text ?? '').trim())
116+ .filter((text) => text !== '')
117+ .join('\n\n')
118+}
@@ -100,3 +100,19 @@ export function activityOf(message: Message): Activity | null {
100 export function visibleDirects(channels: Channel[], agents: string[]): Channel[] {100 export function visibleDirects(channels: Channel[], agents: string[]): Channel[] {
101 return channels.filter((channel) => channel.direct && agents.includes(channel.direct))101 return channels.filter((channel) => channel.direct && agents.includes(channel.direct))
102 }102 }
103+
104+/**
105+ * The text of an answer, for the "copy the answer" button: its text parts,
106+ * as the agent wrote them (Markdown), one paragraph apart. Thoughts, tool
107+ * calls and plans are left out.
108+ *
109+ * @example
110+ * answerText(message) // "Here is the table:\n\n| name | role |…"
111+ */
112+export function answerText(message: Message): string {
113+ return (message.parts ?? [])
114+ .filter((part) => part.kind === 'text')
115+ .map((part) => (part.text ?? '').trim())
116+ .filter((text) => text !== '')
117+ .join('\n\n')
118+}
added frontend/src/lib/errors.test.ts +35 -0
new file mode 100644
@@ -0,0 +1,35 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { explain } from './errors'
5+
6+describe('explain', () => {
7+ it('recognises a program that cannot be found or run', () => {
8+ expect(explain('start agent alice: exec: "mm": executable file not found in $PATH')).toEqual({ key: 'error.programNotFound', vars: { program: 'mm' } })
9+ expect(explain('start agent alice: fork/exec /opt/mm/bin/mm: no such file or directory')).toEqual({ key: 'error.programNotFound', vars: { program: '/opt/mm/bin/mm' } })
10+ expect(explain('start agent alice: fork/exec /tmp/agent.sh: permission denied')).toEqual({ key: 'error.programNotExecutable', vars: { program: '/tmp/agent.sh' } })
11+ })
12+
13+ it('recognises Docker and sbx problems before the agent stopping', () => {
14+ expect(explain('the agent stopped: exit status 1\nError response from daemon: No such container: scout-box')).toEqual({ key: 'error.containerMissing', vars: { box: 'scout-box' } })
15+ expect(explain('Error response from daemon: container 4f2 is not running')).toEqual({ key: 'error.containerStopped', vars: { box: '4f2' } })
16+ expect(explain('the agent stopped: exit status 1\nCannot connect to the Docker daemon at unix:///var/run/docker.sock')).toEqual({ key: 'error.dockerNotRunning', vars: {} })
17+ expect(explain('sandbox "milo-sbx" not found')).toEqual({ key: 'error.sandboxMissing', vars: { box: 'milo-sbx' } })
18+ })
19+
20+ it('recognises the limit of running agents', () => {
21+ expect(explain('too many agents running: 2 agent(s) busy (alice, bob), the limit is 2')).toEqual({ key: 'error.limitReached', vars: { limit: '2' } })
22+ })
23+
24+ it('recognises an agent that stops, does not answer or cannot reach its model', () => {
25+ expect(explain('the agent stopped: exit status 2')?.key).toBe('error.agentStopped')
26+ expect(explain('the agent stopped: exit status 1\ndial tcp 127.0.0.1:11434: connect: connection refused')?.key).toBe('error.modelServer')
27+ expect(explain('no answer to initialize: context deadline exceeded')?.key).toBe('error.noAnswer')
28+ expect(explain('unsupported ACP protocol version: the agent speaks version 2')?.key).toBe('error.protocol')
29+ })
30+
31+ it('leaves unknown errors alone', () => {
32+ expect(explain('something odd happened')).toBeNull()
33+ expect(explain('')).toBeNull()
34+ })
35+})
new file mode 100644
@@ -0,0 +1,35 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { explain } from './errors'
5+
6+describe('explain', () => {
7+ it('recognises a program that cannot be found or run', () => {
8+ expect(explain('start agent alice: exec: "mm": executable file not found in $PATH')).toEqual({ key: 'error.programNotFound', vars: { program: 'mm' } })
9+ expect(explain('start agent alice: fork/exec /opt/mm/bin/mm: no such file or directory')).toEqual({ key: 'error.programNotFound', vars: { program: '/opt/mm/bin/mm' } })
10+ expect(explain('start agent alice: fork/exec /tmp/agent.sh: permission denied')).toEqual({ key: 'error.programNotExecutable', vars: { program: '/tmp/agent.sh' } })
11+ })
12+
13+ it('recognises Docker and sbx problems before the agent stopping', () => {
14+ expect(explain('the agent stopped: exit status 1\nError response from daemon: No such container: scout-box')).toEqual({ key: 'error.containerMissing', vars: { box: 'scout-box' } })
15+ expect(explain('Error response from daemon: container 4f2 is not running')).toEqual({ key: 'error.containerStopped', vars: { box: '4f2' } })
16+ expect(explain('the agent stopped: exit status 1\nCannot connect to the Docker daemon at unix:///var/run/docker.sock')).toEqual({ key: 'error.dockerNotRunning', vars: {} })
17+ expect(explain('sandbox "milo-sbx" not found')).toEqual({ key: 'error.sandboxMissing', vars: { box: 'milo-sbx' } })
18+ })
19+
20+ it('recognises the limit of running agents', () => {
21+ expect(explain('too many agents running: 2 agent(s) busy (alice, bob), the limit is 2')).toEqual({ key: 'error.limitReached', vars: { limit: '2' } })
22+ })
23+
24+ it('recognises an agent that stops, does not answer or cannot reach its model', () => {
25+ expect(explain('the agent stopped: exit status 2')?.key).toBe('error.agentStopped')
26+ expect(explain('the agent stopped: exit status 1\ndial tcp 127.0.0.1:11434: connect: connection refused')?.key).toBe('error.modelServer')
27+ expect(explain('no answer to initialize: context deadline exceeded')?.key).toBe('error.noAnswer')
28+ expect(explain('unsupported ACP protocol version: the agent speaks version 2')?.key).toBe('error.protocol')
29+ })
30+
31+ it('leaves unknown errors alone', () => {
32+ expect(explain('something odd happened')).toBeNull()
33+ expect(explain('')).toBeNull()
34+ })
35+})
added frontend/src/lib/errors.ts +58 -0
new file mode 100644
@@ -0,0 +1,58 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Human error messages (issue #33): the errors of Go, of the agents and of
4+// Docker or sbx are technical. explain recognises the common ones and gives
5+// the message key of a sentence that says what is wrong and what to do; the
6+// technical text stays available, folded, for the curious.
7+
8+import type { MessageKey } from './i18n/index.svelte'
9+
10+export interface Explanation {
11+ key: MessageKey
12+ vars: Record<string, string>
13+}
14+
15+interface Rule {
16+ pattern: RegExp
17+ key: MessageKey
18+ /** Names of the pattern's groups, in order, used as variables. */
19+ groups?: string[]
20+}
21+
22+// The first rule that matches wins: the most specific ones come first.
23+const rules: Rule[] = [
24+ { pattern: /Cannot connect to the Docker daemon|docker daemon is not running|error during connect/i, key: 'error.dockerNotRunning' },
25+ { pattern: /No such container:\s*(\S+)/i, key: 'error.containerMissing', groups: ['box'] },
26+ { pattern: /container (\S+) is not running|Container (\S+) is not running/i, key: 'error.containerStopped', groups: ['box'] },
27+ { pattern: /sandbox[ "']+([\w.-]+)["']? (?:not found|does not exist)/i, key: 'error.sandboxMissing', groups: ['box'] },
28+ { pattern: /exec: "([^"]+)": executable file not found/, key: 'error.programNotFound', groups: ['program'] },
29+ { pattern: /(?:fork\/exec|exec:?) "?([^":\s]+)"?: no such file or directory/, key: 'error.programNotFound', groups: ['program'] },
30+ { pattern: /(?:fork\/exec|exec:?) "?([^":\s]+)"?: permission denied/, key: 'error.programNotExecutable', groups: ['program'] },
31+ { pattern: /too many agents running: .*the limit is (\d+)/, key: 'error.limitReached', groups: ['limit'] },
32+ { pattern: /connection refused|ECONNREFUSED/i, key: 'error.modelServer' },
33+ { pattern: /unsupported ACP protocol version/, key: 'error.protocol' },
34+ { pattern: /no answer to initialize/, key: 'error.noAnswer' },
35+ { pattern: /the agent stopped/, key: 'error.agentStopped' },
36+ { pattern: /unknown agent:?\s*(\S*)/, key: 'error.unknownAgent', groups: ['agent'] },
37+ { pattern: /the history was written by a newer Sidekicks/, key: 'error.newerHistory' },
38+]
39+
40+/**
41+ * The human message for a technical error, or null when it is not a known one.
42+ *
43+ * @example
44+ * explain('start agent alice: exec: "mm": executable file not found in $PATH')
45+ * // { key: 'error.programNotFound', vars: { program: 'mm' } }
46+ */
47+export function explain(raw: string): Explanation | null {
48+ for (const rule of rules) {
49+ const match = rule.pattern.exec(raw)
50+ if (!match) continue
51+ const vars: Record<string, string> = {}
52+ // Alternatives may capture into different groups: take the first non-empty one per name.
53+ const captured = match.slice(1).filter((value) => value !== undefined)
54+ ;(rule.groups ?? []).forEach((name, index) => (vars[name] = captured[index] ?? ''))
55+ return { key: rule.key, vars }
56+ }
57+ return null
58+}
new file mode 100644
@@ -0,0 +1,58 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Human error messages (issue #33): the errors of Go, of the agents and of
4+// Docker or sbx are technical. explain recognises the common ones and gives
5+// the message key of a sentence that says what is wrong and what to do; the
6+// technical text stays available, folded, for the curious.
7+
8+import type { MessageKey } from './i18n/index.svelte'
9+
10+export interface Explanation {
11+ key: MessageKey
12+ vars: Record<string, string>
13+}
14+
15+interface Rule {
16+ pattern: RegExp
17+ key: MessageKey
18+ /** Names of the pattern's groups, in order, used as variables. */
19+ groups?: string[]
20+}
21+
22+// The first rule that matches wins: the most specific ones come first.
23+const rules: Rule[] = [
24+ { pattern: /Cannot connect to the Docker daemon|docker daemon is not running|error during connect/i, key: 'error.dockerNotRunning' },
25+ { pattern: /No such container:\s*(\S+)/i, key: 'error.containerMissing', groups: ['box'] },
26+ { pattern: /container (\S+) is not running|Container (\S+) is not running/i, key: 'error.containerStopped', groups: ['box'] },
27+ { pattern: /sandbox[ "']+([\w.-]+)["']? (?:not found|does not exist)/i, key: 'error.sandboxMissing', groups: ['box'] },
28+ { pattern: /exec: "([^"]+)": executable file not found/, key: 'error.programNotFound', groups: ['program'] },
29+ { pattern: /(?:fork\/exec|exec:?) "?([^":\s]+)"?: no such file or directory/, key: 'error.programNotFound', groups: ['program'] },
30+ { pattern: /(?:fork\/exec|exec:?) "?([^":\s]+)"?: permission denied/, key: 'error.programNotExecutable', groups: ['program'] },
31+ { pattern: /too many agents running: .*the limit is (\d+)/, key: 'error.limitReached', groups: ['limit'] },
32+ { pattern: /connection refused|ECONNREFUSED/i, key: 'error.modelServer' },
33+ { pattern: /unsupported ACP protocol version/, key: 'error.protocol' },
34+ { pattern: /no answer to initialize/, key: 'error.noAnswer' },
35+ { pattern: /the agent stopped/, key: 'error.agentStopped' },
36+ { pattern: /unknown agent:?\s*(\S*)/, key: 'error.unknownAgent', groups: ['agent'] },
37+ { pattern: /the history was written by a newer Sidekicks/, key: 'error.newerHistory' },
38+]
39+
40+/**
41+ * The human message for a technical error, or null when it is not a known one.
42+ *
43+ * @example
44+ * explain('start agent alice: exec: "mm": executable file not found in $PATH')
45+ * // { key: 'error.programNotFound', vars: { program: 'mm' } }
46+ */
47+export function explain(raw: string): Explanation | null {
48+ for (const rule of rules) {
49+ const match = rule.pattern.exec(raw)
50+ if (!match) continue
51+ const vars: Record<string, string> = {}
52+ // Alternatives may capture into different groups: take the first non-empty one per name.
53+ const captured = match.slice(1).filter((value) => value !== undefined)
54+ ;(rule.groups ?? []).forEach((name, index) => (vars[name] = captured[index] ?? ''))
55+ return { key: rule.key, vars }
56+ }
57+ return null
58+}
modified frontend/src/lib/i18n/messages.ts +167 -12
@@ -19,7 +19,7 @@ export const en = {
1919 'agent.state.busy': 'busy',
2020 'agent.state.error': 'error',
2121 'agent.outdated': ' · restart to apply changes',
22- 'agents.empty': 'No agent yet. Add one in Settings.',
22+ 'agents.empty': 'No agent yet.',
2323 'agent.start': 'Start {agent}',
2424 'agent.restart': 'Restart {agent}',
2525 'agent.stop': 'Stop {agent}',
@@ -59,8 +59,6 @@ export const en = {
5959 'conversation.stuck': '{agent} is going in circles: {count} tools failed in a row.',
6060 'conversation.stop': '■ Stop {agent}',
6161 'conversation.placeholder': 'Write to {agent}…',
62- 'conversation.noAgents': 'Start by adding an agent to the configuration (Settings panel).',
63- 'conversation.pickAgent': 'Pick a channel, or click an agent to talk with it directly.',
6462 'conversation.channelEmpty': 'Mention an agent with @ to bring it into #{channel}. {description}',
6563 'conversation.channelPlaceholder': 'Write in #{channel} — @ to mention an agent, / for commands',
6664 'conversation.answeringAgent': '{agent} is answering…',
@@ -92,7 +90,7 @@ export const en = {
9290 'message.stop.max_tokens': 'Answer cut: the agent reached its length limit.',
9391 'message.stop.max_turn_requests': 'Answer cut: the agent reached its maximum number of steps.',
9492 'message.stop.refusal': 'The agent refused to answer.',
95- 'message.error': 'The answer ended with an error: {error}',
93+ 'message.error': 'The answer ended with an error.',
9694 'message.notSent': 'Message not sent (answer stopped).',
9795 'system.agent-joined': '{agent} joined the channel.',
9896 'system.agent-left': '{agent} left the channel.',
@@ -130,8 +128,23 @@ export const en = {
130128
131129 // code blocks
132130 'error.dismiss': 'Close this message',
131+ 'error.details': 'Technical details',
132+ 'error.programNotFound': 'The program "{program}" cannot be found. Install it, or give its full path in the agent\'s settings (for example /opt/homebrew/bin/{program}).',
133+ 'error.programNotExecutable': '"{program}" cannot be run: it is not executable. In a terminal: chmod +x {program}',
134+ 'error.dockerNotRunning': 'Docker is not running. Start Docker Desktop (or the Docker service), then try again.',
135+ 'error.containerMissing': 'The container "{box}" does not exist. Create and start it (docker run …), or correct its name in the agent\'s settings.',
136+ 'error.containerStopped': 'The container "{box}" is stopped. Start it with: docker start {box}',
137+ 'error.sandboxMissing': 'The sbx sandbox "{box}" does not exist. Create it, or correct its name in the agent\'s settings.',
138+ 'error.limitReached': 'Already {limit} agents are working, the most allowed at once. Wait for one to finish, or stop one in the Agents panel.',
139+ 'error.modelServer': 'The agent cannot reach its model server. Check that it is running (llmman, Ollama…), then try again.',
140+ 'error.protocol': 'This agent speaks a version of ACP that Sidekicks does not know. Update the agent or Sidekicks.',
141+ 'error.noAnswer': 'The agent did not answer when Sidekicks contacted it. Check that its program speaks ACP (for mm: the -acp argument).',
142+ 'error.agentStopped': 'The agent stopped unexpectedly. Its log (📂, then Log) tells why.',
143+ 'error.unknownAgent': 'The agent "{agent}" is no longer in the configuration.',
144+ 'error.newerHistory': 'The history was written by a newer Sidekicks. Update Sidekicks to read it.',
133145 'code.copy': 'Copy',
134146 'code.copied': 'Copied ✓',
147+ 'message.copy': 'Copy the answer',
135148
136149 // settings
137150 'settings.source.flag': '--config option',
@@ -182,10 +195,75 @@ export const en = {
182195 'workspace.reload': 'Take their version',
183196 'workspace.overwrite': 'Keep mine',
184197 'workspace.cannotOpen': 'This file cannot be opened here: {error}',
185- 'workspace.unavailable': 'The files of {agent} cannot be shown: {problem}',
198+ 'workspace.unavailable': 'The files of {agent} cannot be shown.',
186199 'terminal.ended': 'The shell has ended.',
187200 'terminal.restart': 'Open a new shell',
188- 'terminal.error': 'Cannot open a terminal: {error}',
201+ 'terminal.error': 'Cannot open a terminal.',
202+
203+ // welcome and the "add an agent" assistant
204+ 'welcome.title': 'Welcome to Sidekicks',
205+ 'welcome.tagline': 'Talk with AI agents in channels, as you would with colleagues.',
206+ 'welcome.step1.title': 'Add an agent',
207+ 'welcome.step1.body': 'An assistant that runs on your computer, in Docker or in an sbx sandbox.',
208+ 'welcome.step2.title': 'Open a channel',
209+ 'welcome.step2.body': '#general is ready. Create others, one per topic.',
210+ 'welcome.step3.title': 'Call it with @',
211+ 'welcome.step3.body': '"@alice hello": Alice joins the channel and answers you.',
212+ 'welcome.addFirst': '+ Add my first agent',
213+ 'welcome.openConfig': 'Open a configuration…',
214+ 'welcome.configAt': 'Configuration: {path}',
215+ 'welcome.pick': 'Pick a channel, or click an agent to talk with it directly.',
216+ 'agents.add': '+ Add an agent',
217+ 'palette.label': 'Command palette',
218+ 'palette.placeholder': 'Go to a channel or an agent, or run an action…',
219+ 'palette.none': 'Nothing matches.',
220+ 'palette.channel': 'channel',
221+ 'palette.direct': 'conversation',
222+ 'palette.files': 'files',
223+ 'palette.filesOf': 'Files of {agent}',
224+ 'action.newChannel': 'New channel',
225+ 'action.addAgent': 'Add an agent',
226+ 'action.focusComposer': 'Write a message',
227+ 'action.leftDock': 'Show or hide channels and agents',
228+ 'action.settings': 'Show or hide Settings',
229+ 'action.log': 'Show or hide the log',
230+ 'action.openConfig': 'Open a configuration…',
231+ 'action.palette': 'Command palette',
232+ 'assistant.title': 'Add an agent',
233+ 'assistant.intro': 'Sidekicks writes the agent into your configuration for you. You can change it later in Settings.',
234+ 'assistant.who': 'Who is it?',
235+ 'assistant.agentTitle': 'Name shown',
236+ 'assistant.agentTitlePlaceholder': 'Alice (assistant)',
237+ 'assistant.name': 'Name used with @',
238+ 'assistant.nameHint': 'Lowercase letters, digits, - and _; it starts with a letter.',
239+ 'assistant.icon': 'Icon',
240+ 'assistant.description': 'What it does',
241+ 'assistant.descriptionPlaceholder': 'Answers simple questions',
242+ 'assistant.where': 'Where does it run?',
243+ 'assistant.local': 'On this computer',
244+ 'assistant.container': 'In a Docker container',
245+ 'assistant.sbx': 'In an sbx sandbox',
246+ 'assistant.containerName': 'Container name',
247+ 'assistant.sandboxName': 'Sandbox name',
248+ 'assistant.boxHint': 'It must be running, with the agent\'s folder mounted at the same path.',
249+ 'assistant.how': 'How is it started?',
250+ 'assistant.program': 'Program',
251+ 'assistant.programPlaceholder': 'mm',
252+ 'assistant.programHint': 'The agent\'s program, as you would type it in a terminal. It must speak ACP.',
253+ 'assistant.arguments': 'Arguments',
254+ 'assistant.argumentsPlaceholder': '-acp -config agent.yaml',
255+ 'assistant.workspace': 'Agent\'s folder',
256+ 'assistant.workspaceHint': 'The agent works there; it is created if it does not exist.',
257+ 'assistant.choose': 'Choose…',
258+ 'assistant.add': 'Add the agent',
259+ 'assistant.cancel': 'Cancel',
260+ 'assistant.adding': 'Adding…',
261+ 'assistant.missing.name': 'Give the agent a name.',
262+ 'assistant.missing.nameFormat': 'The name used with @ may only hold lowercase letters, digits, - and _, and starts with a letter.',
263+ 'assistant.missing.program': 'Say which program starts the agent.',
264+ 'assistant.missing.workspace': 'Choose the agent\'s folder.',
265+ 'assistant.missing.box': 'Give the name of the container or sandbox.',
266+ 'assistant.refused': 'The agent was not added:',
189267 } as const
190268
191269 export type MessageKey = keyof typeof en
@@ -203,7 +281,7 @@ export const fr: Record<MessageKey, string> = {
203281 'agent.state.busy': 'occupé',
204282 'agent.state.error': 'erreur',
205283 'agent.outdated': ' · à redémarrer',
206- 'agents.empty': 'Aucun agent. Ajoutez-en un dans Settings.',
284+ 'agents.empty': 'Aucun agent pour le moment.',
207285 'agent.start': 'Démarrer {agent}',
208286 'agent.restart': 'Redémarrer {agent}',
209287 'agent.stop': 'Arrêter {agent}',
@@ -239,8 +317,6 @@ export const fr: Record<MessageKey, string> = {
239317 'conversation.stuck': "{agent} tourne en rond : {count} outils ont échoué d'affilée.",
240318 'conversation.stop': '■ Arrêter {agent}',
241319 'conversation.placeholder': 'Écrivez à {agent}…',
242- 'conversation.noAgents': 'Commencez par ajouter un agent dans la configuration (panneau Settings).',
243- 'conversation.pickAgent': 'Choisissez un channel, ou cliquez sur un agent pour lui parler directement.',
244320 'conversation.channelEmpty': 'Mentionnez un agent avec @ pour le faire venir dans #{channel}. {description}',
245321 'conversation.channelPlaceholder': 'Écrivez dans #{channel} — @ pour mentionner un agent, / pour les commandes',
246322 'conversation.answeringAgent': '{agent} répond…',
@@ -270,7 +346,7 @@ export const fr: Record<MessageKey, string> = {
270346 'message.stop.max_tokens': "Réponse coupée : l'agent a atteint sa limite de longueur.",
271347 'message.stop.max_turn_requests': "Réponse coupée : l'agent a atteint son nombre maximal d'étapes.",
272348 'message.stop.refusal': "L'agent a refusé de répondre.",
273- 'message.error': "La réponse s'est terminée sur une erreur : {error}",
349+ 'message.error': "La réponse s'est terminée sur une erreur.",
274350 'message.notSent': 'Message non envoyé (réponse arrêtée).',
275351 'system.agent-joined': '{agent} a rejoint le channel.',
276352 'system.agent-left': '{agent} a quitté le channel.',
@@ -305,8 +381,23 @@ export const fr: Record<MessageKey, string> = {
305381 'permission.details': 'Détails techniques',
306382
307383 'error.dismiss': 'Fermer ce message',
384+ 'error.details': 'Détails techniques',
385+ 'error.programNotFound': "Le programme « {program} » est introuvable. Installez-le, ou indiquez son chemin complet dans les réglages de l'agent (par exemple /opt/homebrew/bin/{program}).",
386+ 'error.programNotExecutable': "« {program} » ne peut pas être lancé : il n'est pas exécutable. Dans un terminal : chmod +x {program}",
387+ 'error.dockerNotRunning': "Docker n'est pas démarré. Lancez Docker Desktop (ou le service Docker), puis réessayez.",
388+ 'error.containerMissing': "Le conteneur « {box} » n'existe pas. Créez-le et démarrez-le (docker run …), ou corrigez son nom dans les réglages de l'agent.",
389+ 'error.containerStopped': 'Le conteneur « {box} » est arrêté. Démarrez-le avec : docker start {box}',
390+ 'error.sandboxMissing': "La sandbox sbx « {box} » n'existe pas. Créez-la, ou corrigez son nom dans les réglages de l'agent.",
391+ 'error.limitReached': "Déjà {limit} agents travaillent, le maximum autorisé en même temps. Attendez qu'un agent ait fini, ou arrêtez-en un dans le panneau Agents.",
392+ 'error.modelServer': "L'agent n'arrive pas à joindre son serveur de modèles. Vérifiez qu'il tourne (llmman, Ollama…), puis réessayez.",
393+ 'error.protocol': "Cet agent parle une version d'ACP que Sidekicks ne connaît pas. Mettez à jour l'agent ou Sidekicks.",
394+ 'error.noAnswer': "L'agent n'a pas répondu quand Sidekicks l'a contacté. Vérifiez que son programme parle ACP (pour mm : l'argument -acp).",
395+ 'error.agentStopped': "L'agent s'est arrêté de façon inattendue. Son journal (📂, puis Journal) en donne la raison.",
396+ 'error.unknownAgent': "L'agent « {agent} » n'est plus dans la configuration.",
397+ 'error.newerHistory': "L'historique a été écrit par un Sidekicks plus récent. Mettez Sidekicks à jour pour le lire.",
308398 'code.copy': 'Copier',
309399 'code.copied': 'Copié ✓',
400+ 'message.copy': 'Copier la réponse',
310401
311402 'settings.source.flag': 'option --config',
312403 'settings.source.environment': 'variable SIDEKICKS_CONFIG',
@@ -356,8 +447,72 @@ export const fr: Record<MessageKey, string> = {
356447 'workspace.reload': 'Prendre sa version',
357448 'workspace.overwrite': 'Garder la mienne',
358449 'workspace.cannotOpen': "Ce fichier ne peut pas être ouvert ici : {error}",
359- 'workspace.unavailable': "Les fichiers de {agent} ne peuvent pas être affichés : {problem}",
450+ 'workspace.unavailable': 'Les fichiers de {agent} ne peuvent pas être affichés.',
360451 'terminal.ended': 'Le shell est terminé.',
361452 'terminal.restart': 'Ouvrir un nouveau shell',
362- 'terminal.error': "Impossible d'ouvrir un terminal : {error}",
453+ 'terminal.error': "Impossible d'ouvrir un terminal.",
454+
455+ 'welcome.title': 'Bienvenue dans Sidekicks',
456+ 'welcome.tagline': "Discutez avec des agents d'IA dans des channels, comme avec des collègues.",
457+ 'welcome.step1.title': 'Ajoutez un agent',
458+ 'welcome.step1.body': 'Un assistant qui tourne sur votre ordinateur, dans Docker ou dans une sandbox sbx.',
459+ 'welcome.step2.title': 'Ouvrez un channel',
460+ 'welcome.step2.body': "#general est prêt. Créez-en d'autres, un par sujet.",
461+ 'welcome.step3.title': 'Appelez-le avec @',
462+ 'welcome.step3.body': '« @alice bonjour » : Alice rejoint le channel et vous répond.',
463+ 'welcome.addFirst': '+ Ajouter mon premier agent',
464+ 'welcome.openConfig': 'Ouvrir une configuration…',
465+ 'welcome.configAt': 'Configuration : {path}',
466+ 'welcome.pick': 'Choisissez un channel, ou cliquez sur un agent pour lui parler directement.',
467+ 'agents.add': '+ Ajouter un agent',
468+ 'palette.label': 'Palette de commandes',
469+ 'palette.placeholder': 'Aller à un channel ou un agent, ou lancer une action…',
470+ 'palette.none': 'Aucun résultat.',
471+ 'palette.channel': 'channel',
472+ 'palette.direct': 'conversation',
473+ 'palette.files': 'fichiers',
474+ 'palette.filesOf': 'Fichiers de {agent}',
475+ 'action.newChannel': 'Nouveau channel',
476+ 'action.addAgent': 'Ajouter un agent',
477+ 'action.focusComposer': 'Écrire un message',
478+ 'action.leftDock': 'Afficher ou masquer channels et agents',
479+ 'action.settings': 'Afficher ou masquer Settings',
480+ 'action.log': 'Afficher ou masquer le journal',
481+ 'action.openConfig': 'Ouvrir une configuration…',
482+ 'action.palette': 'Palette de commandes',
483+ 'assistant.title': 'Ajouter un agent',
484+ 'assistant.intro': "Sidekicks écrit l'agent dans votre configuration à votre place. Vous pourrez le modifier dans Settings.",
485+ 'assistant.who': 'Qui est-ce ?',
486+ 'assistant.agentTitle': 'Nom affiché',
487+ 'assistant.agentTitlePlaceholder': 'Alice (assistante)',
488+ 'assistant.name': 'Nom utilisé avec @',
489+ 'assistant.nameHint': 'Minuscules, chiffres, - et _ ; commence par une lettre.',
490+ 'assistant.icon': 'Icône',
491+ 'assistant.description': 'Ce qu\'il fait',
492+ 'assistant.descriptionPlaceholder': 'Répond aux questions simples',
493+ 'assistant.where': 'Où tourne-t-il ?',
494+ 'assistant.local': 'Sur cet ordinateur',
495+ 'assistant.container': 'Dans un conteneur Docker',
496+ 'assistant.sbx': 'Dans une sandbox sbx',
497+ 'assistant.containerName': 'Nom du conteneur',
498+ 'assistant.sandboxName': 'Nom de la sandbox',
499+ 'assistant.boxHint': "Elle doit être démarrée, avec le dossier de l'agent monté au même chemin.",
500+ 'assistant.how': 'Comment le démarrer ?',
501+ 'assistant.program': 'Programme',
502+ 'assistant.programPlaceholder': 'mm',
503+ 'assistant.programHint': "Le programme de l'agent, tel que vous le taperiez dans un terminal. Il doit parler ACP.",
504+ 'assistant.arguments': 'Arguments',
505+ 'assistant.argumentsPlaceholder': '-acp -config agent.yaml',
506+ 'assistant.workspace': "Dossier de l'agent",
507+ 'assistant.workspaceHint': "L'agent y travaille ; il est créé s'il n'existe pas.",
508+ 'assistant.choose': 'Choisir…',
509+ 'assistant.add': "Ajouter l'agent",
510+ 'assistant.cancel': 'Annuler',
511+ 'assistant.adding': 'Ajout…',
512+ 'assistant.missing.name': "Donnez un nom à l'agent.",
513+ 'assistant.missing.nameFormat': 'Le nom utilisé avec @ ne contient que des minuscules, des chiffres, - et _, et commence par une lettre.',
514+ 'assistant.missing.program': "Indiquez le programme qui démarre l'agent.",
515+ 'assistant.missing.workspace': "Choisissez le dossier de l'agent.",
516+ 'assistant.missing.box': 'Donnez le nom du conteneur ou de la sandbox.',
517+ 'assistant.refused': "L'agent n'a pas été ajouté :",
363518 }
@@ -19,7 +19,7 @@ export const en = {
19 'agent.state.busy': 'busy',19 'agent.state.busy': 'busy',
20 'agent.state.error': 'error',20 'agent.state.error': 'error',
21 'agent.outdated': ' · restart to apply changes',21 'agent.outdated': ' · restart to apply changes',
22- 'agents.empty': 'No agent yet. Add one in Settings.',22+ 'agents.empty': 'No agent yet.',
23 'agent.start': 'Start {agent}',23 'agent.start': 'Start {agent}',
24 'agent.restart': 'Restart {agent}',24 'agent.restart': 'Restart {agent}',
25 'agent.stop': 'Stop {agent}',25 'agent.stop': 'Stop {agent}',
@@ -59,8 +59,6 @@ export const en = {
59 'conversation.stuck': '{agent} is going in circles: {count} tools failed in a row.',59 'conversation.stuck': '{agent} is going in circles: {count} tools failed in a row.',
60 'conversation.stop': '■ Stop {agent}',60 'conversation.stop': '■ Stop {agent}',
61 'conversation.placeholder': 'Write to {agent}…',61 'conversation.placeholder': 'Write to {agent}…',
62- 'conversation.noAgents': 'Start by adding an agent to the configuration (Settings panel).',
63- 'conversation.pickAgent': 'Pick a channel, or click an agent to talk with it directly.',
64 'conversation.channelEmpty': 'Mention an agent with @ to bring it into #{channel}. {description}',62 'conversation.channelEmpty': 'Mention an agent with @ to bring it into #{channel}. {description}',
65 'conversation.channelPlaceholder': 'Write in #{channel} — @ to mention an agent, / for commands',63 'conversation.channelPlaceholder': 'Write in #{channel} — @ to mention an agent, / for commands',
66 'conversation.answeringAgent': '{agent} is answering…',64 'conversation.answeringAgent': '{agent} is answering…',
@@ -92,7 +90,7 @@ export const en = {
92 'message.stop.max_tokens': 'Answer cut: the agent reached its length limit.',90 'message.stop.max_tokens': 'Answer cut: the agent reached its length limit.',
93 'message.stop.max_turn_requests': 'Answer cut: the agent reached its maximum number of steps.',91 'message.stop.max_turn_requests': 'Answer cut: the agent reached its maximum number of steps.',
94 'message.stop.refusal': 'The agent refused to answer.',92 'message.stop.refusal': 'The agent refused to answer.',
95- 'message.error': 'The answer ended with an error: {error}',93+ 'message.error': 'The answer ended with an error.',
96 'message.notSent': 'Message not sent (answer stopped).',94 'message.notSent': 'Message not sent (answer stopped).',
97 'system.agent-joined': '{agent} joined the channel.',95 'system.agent-joined': '{agent} joined the channel.',
98 'system.agent-left': '{agent} left the channel.',96 'system.agent-left': '{agent} left the channel.',
@@ -130,8 +128,23 @@ export const en = {
130 128
131 // code blocks129 // code blocks
132 'error.dismiss': 'Close this message',130 'error.dismiss': 'Close this message',
131+ 'error.details': 'Technical details',
132+ 'error.programNotFound': 'The program "{program}" cannot be found. Install it, or give its full path in the agent\'s settings (for example /opt/homebrew/bin/{program}).',
133+ 'error.programNotExecutable': '"{program}" cannot be run: it is not executable. In a terminal: chmod +x {program}',
134+ 'error.dockerNotRunning': 'Docker is not running. Start Docker Desktop (or the Docker service), then try again.',
135+ 'error.containerMissing': 'The container "{box}" does not exist. Create and start it (docker run …), or correct its name in the agent\'s settings.',
136+ 'error.containerStopped': 'The container "{box}" is stopped. Start it with: docker start {box}',
137+ 'error.sandboxMissing': 'The sbx sandbox "{box}" does not exist. Create it, or correct its name in the agent\'s settings.',
138+ 'error.limitReached': 'Already {limit} agents are working, the most allowed at once. Wait for one to finish, or stop one in the Agents panel.',
139+ 'error.modelServer': 'The agent cannot reach its model server. Check that it is running (llmman, Ollama…), then try again.',
140+ 'error.protocol': 'This agent speaks a version of ACP that Sidekicks does not know. Update the agent or Sidekicks.',
141+ 'error.noAnswer': 'The agent did not answer when Sidekicks contacted it. Check that its program speaks ACP (for mm: the -acp argument).',
142+ 'error.agentStopped': 'The agent stopped unexpectedly. Its log (📂, then Log) tells why.',
143+ 'error.unknownAgent': 'The agent "{agent}" is no longer in the configuration.',
144+ 'error.newerHistory': 'The history was written by a newer Sidekicks. Update Sidekicks to read it.',
133 'code.copy': 'Copy',145 'code.copy': 'Copy',
134 'code.copied': 'Copied ✓',146 'code.copied': 'Copied ✓',
147+ 'message.copy': 'Copy the answer',
135 148
136 // settings149 // settings
137 'settings.source.flag': '--config option',150 'settings.source.flag': '--config option',
@@ -182,10 +195,75 @@ export const en = {
182 'workspace.reload': 'Take their version',195 'workspace.reload': 'Take their version',
183 'workspace.overwrite': 'Keep mine',196 'workspace.overwrite': 'Keep mine',
184 'workspace.cannotOpen': 'This file cannot be opened here: {error}',197 'workspace.cannotOpen': 'This file cannot be opened here: {error}',
185- 'workspace.unavailable': 'The files of {agent} cannot be shown: {problem}',198+ 'workspace.unavailable': 'The files of {agent} cannot be shown.',
186 'terminal.ended': 'The shell has ended.',199 'terminal.ended': 'The shell has ended.',
187 'terminal.restart': 'Open a new shell',200 'terminal.restart': 'Open a new shell',
188- 'terminal.error': 'Cannot open a terminal: {error}',201+ 'terminal.error': 'Cannot open a terminal.',
202+
203+ // welcome and the "add an agent" assistant
204+ 'welcome.title': 'Welcome to Sidekicks',
205+ 'welcome.tagline': 'Talk with AI agents in channels, as you would with colleagues.',
206+ 'welcome.step1.title': 'Add an agent',
207+ 'welcome.step1.body': 'An assistant that runs on your computer, in Docker or in an sbx sandbox.',
208+ 'welcome.step2.title': 'Open a channel',
209+ 'welcome.step2.body': '#general is ready. Create others, one per topic.',
210+ 'welcome.step3.title': 'Call it with @',
211+ 'welcome.step3.body': '"@alice hello": Alice joins the channel and answers you.',
212+ 'welcome.addFirst': '+ Add my first agent',
213+ 'welcome.openConfig': 'Open a configuration…',
214+ 'welcome.configAt': 'Configuration: {path}',
215+ 'welcome.pick': 'Pick a channel, or click an agent to talk with it directly.',
216+ 'agents.add': '+ Add an agent',
217+ 'palette.label': 'Command palette',
218+ 'palette.placeholder': 'Go to a channel or an agent, or run an action…',
219+ 'palette.none': 'Nothing matches.',
220+ 'palette.channel': 'channel',
221+ 'palette.direct': 'conversation',
222+ 'palette.files': 'files',
223+ 'palette.filesOf': 'Files of {agent}',
224+ 'action.newChannel': 'New channel',
225+ 'action.addAgent': 'Add an agent',
226+ 'action.focusComposer': 'Write a message',
227+ 'action.leftDock': 'Show or hide channels and agents',
228+ 'action.settings': 'Show or hide Settings',
229+ 'action.log': 'Show or hide the log',
230+ 'action.openConfig': 'Open a configuration…',
231+ 'action.palette': 'Command palette',
232+ 'assistant.title': 'Add an agent',
233+ 'assistant.intro': 'Sidekicks writes the agent into your configuration for you. You can change it later in Settings.',
234+ 'assistant.who': 'Who is it?',
235+ 'assistant.agentTitle': 'Name shown',
236+ 'assistant.agentTitlePlaceholder': 'Alice (assistant)',
237+ 'assistant.name': 'Name used with @',
238+ 'assistant.nameHint': 'Lowercase letters, digits, - and _; it starts with a letter.',
239+ 'assistant.icon': 'Icon',
240+ 'assistant.description': 'What it does',
241+ 'assistant.descriptionPlaceholder': 'Answers simple questions',
242+ 'assistant.where': 'Where does it run?',
243+ 'assistant.local': 'On this computer',
244+ 'assistant.container': 'In a Docker container',
245+ 'assistant.sbx': 'In an sbx sandbox',
246+ 'assistant.containerName': 'Container name',
247+ 'assistant.sandboxName': 'Sandbox name',
248+ 'assistant.boxHint': 'It must be running, with the agent\'s folder mounted at the same path.',
249+ 'assistant.how': 'How is it started?',
250+ 'assistant.program': 'Program',
251+ 'assistant.programPlaceholder': 'mm',
252+ 'assistant.programHint': 'The agent\'s program, as you would type it in a terminal. It must speak ACP.',
253+ 'assistant.arguments': 'Arguments',
254+ 'assistant.argumentsPlaceholder': '-acp -config agent.yaml',
255+ 'assistant.workspace': 'Agent\'s folder',
256+ 'assistant.workspaceHint': 'The agent works there; it is created if it does not exist.',
257+ 'assistant.choose': 'Choose…',
258+ 'assistant.add': 'Add the agent',
259+ 'assistant.cancel': 'Cancel',
260+ 'assistant.adding': 'Adding…',
261+ 'assistant.missing.name': 'Give the agent a name.',
262+ 'assistant.missing.nameFormat': 'The name used with @ may only hold lowercase letters, digits, - and _, and starts with a letter.',
263+ 'assistant.missing.program': 'Say which program starts the agent.',
264+ 'assistant.missing.workspace': 'Choose the agent\'s folder.',
265+ 'assistant.missing.box': 'Give the name of the container or sandbox.',
266+ 'assistant.refused': 'The agent was not added:',
189 } as const267 } as const
190 268
191 export type MessageKey = keyof typeof en269 export type MessageKey = keyof typeof en
@@ -203,7 +281,7 @@ export const fr: Record<MessageKey, string> = {
203 'agent.state.busy': 'occupé',281 'agent.state.busy': 'occupé',
204 'agent.state.error': 'erreur',282 'agent.state.error': 'erreur',
205 'agent.outdated': ' · à redémarrer',283 'agent.outdated': ' · à redémarrer',
206- 'agents.empty': 'Aucun agent. Ajoutez-en un dans Settings.',284+ 'agents.empty': 'Aucun agent pour le moment.',
207 'agent.start': 'Démarrer {agent}',285 'agent.start': 'Démarrer {agent}',
208 'agent.restart': 'Redémarrer {agent}',286 'agent.restart': 'Redémarrer {agent}',
209 'agent.stop': 'Arrêter {agent}',287 'agent.stop': 'Arrêter {agent}',
@@ -239,8 +317,6 @@ export const fr: Record<MessageKey, string> = {
239 'conversation.stuck': "{agent} tourne en rond : {count} outils ont échoué d'affilée.",317 'conversation.stuck': "{agent} tourne en rond : {count} outils ont échoué d'affilée.",
240 'conversation.stop': '■ Arrêter {agent}',318 'conversation.stop': '■ Arrêter {agent}',
241 'conversation.placeholder': 'Écrivez à {agent}…',319 'conversation.placeholder': 'Écrivez à {agent}…',
242- 'conversation.noAgents': 'Commencez par ajouter un agent dans la configuration (panneau Settings).',
243- 'conversation.pickAgent': 'Choisissez un channel, ou cliquez sur un agent pour lui parler directement.',
244 'conversation.channelEmpty': 'Mentionnez un agent avec @ pour le faire venir dans #{channel}. {description}',320 'conversation.channelEmpty': 'Mentionnez un agent avec @ pour le faire venir dans #{channel}. {description}',
245 'conversation.channelPlaceholder': 'Écrivez dans #{channel} — @ pour mentionner un agent, / pour les commandes',321 'conversation.channelPlaceholder': 'Écrivez dans #{channel} — @ pour mentionner un agent, / pour les commandes',
246 'conversation.answeringAgent': '{agent} répond…',322 'conversation.answeringAgent': '{agent} répond…',
@@ -270,7 +346,7 @@ export const fr: Record<MessageKey, string> = {
270 'message.stop.max_tokens': "Réponse coupée : l'agent a atteint sa limite de longueur.",346 'message.stop.max_tokens': "Réponse coupée : l'agent a atteint sa limite de longueur.",
271 'message.stop.max_turn_requests': "Réponse coupée : l'agent a atteint son nombre maximal d'étapes.",347 'message.stop.max_turn_requests': "Réponse coupée : l'agent a atteint son nombre maximal d'étapes.",
272 'message.stop.refusal': "L'agent a refusé de répondre.",348 'message.stop.refusal': "L'agent a refusé de répondre.",
273- 'message.error': "La réponse s'est terminée sur une erreur : {error}",349+ 'message.error': "La réponse s'est terminée sur une erreur.",
274 'message.notSent': 'Message non envoyé (réponse arrêtée).',350 'message.notSent': 'Message non envoyé (réponse arrêtée).',
275 'system.agent-joined': '{agent} a rejoint le channel.',351 'system.agent-joined': '{agent} a rejoint le channel.',
276 'system.agent-left': '{agent} a quitté le channel.',352 'system.agent-left': '{agent} a quitté le channel.',
@@ -305,8 +381,23 @@ export const fr: Record<MessageKey, string> = {
305 'permission.details': 'Détails techniques',381 'permission.details': 'Détails techniques',
306 382
307 'error.dismiss': 'Fermer ce message',383 'error.dismiss': 'Fermer ce message',
384+ 'error.details': 'Détails techniques',
385+ 'error.programNotFound': "Le programme « {program} » est introuvable. Installez-le, ou indiquez son chemin complet dans les réglages de l'agent (par exemple /opt/homebrew/bin/{program}).",
386+ 'error.programNotExecutable': "« {program} » ne peut pas être lancé : il n'est pas exécutable. Dans un terminal : chmod +x {program}",
387+ 'error.dockerNotRunning': "Docker n'est pas démarré. Lancez Docker Desktop (ou le service Docker), puis réessayez.",
388+ 'error.containerMissing': "Le conteneur « {box} » n'existe pas. Créez-le et démarrez-le (docker run …), ou corrigez son nom dans les réglages de l'agent.",
389+ 'error.containerStopped': 'Le conteneur « {box} » est arrêté. Démarrez-le avec : docker start {box}',
390+ 'error.sandboxMissing': "La sandbox sbx « {box} » n'existe pas. Créez-la, ou corrigez son nom dans les réglages de l'agent.",
391+ 'error.limitReached': "Déjà {limit} agents travaillent, le maximum autorisé en même temps. Attendez qu'un agent ait fini, ou arrêtez-en un dans le panneau Agents.",
392+ 'error.modelServer': "L'agent n'arrive pas à joindre son serveur de modèles. Vérifiez qu'il tourne (llmman, Ollama…), puis réessayez.",
393+ 'error.protocol': "Cet agent parle une version d'ACP que Sidekicks ne connaît pas. Mettez à jour l'agent ou Sidekicks.",
394+ 'error.noAnswer': "L'agent n'a pas répondu quand Sidekicks l'a contacté. Vérifiez que son programme parle ACP (pour mm : l'argument -acp).",
395+ 'error.agentStopped': "L'agent s'est arrêté de façon inattendue. Son journal (📂, puis Journal) en donne la raison.",
396+ 'error.unknownAgent': "L'agent « {agent} » n'est plus dans la configuration.",
397+ 'error.newerHistory': "L'historique a été écrit par un Sidekicks plus récent. Mettez Sidekicks à jour pour le lire.",
308 'code.copy': 'Copier',398 'code.copy': 'Copier',
309 'code.copied': 'Copié ✓',399 'code.copied': 'Copié ✓',
400+ 'message.copy': 'Copier la réponse',
310 401
311 'settings.source.flag': 'option --config',402 'settings.source.flag': 'option --config',
312 'settings.source.environment': 'variable SIDEKICKS_CONFIG',403 'settings.source.environment': 'variable SIDEKICKS_CONFIG',
@@ -356,8 +447,72 @@ export const fr: Record<MessageKey, string> = {
356 'workspace.reload': 'Prendre sa version',447 'workspace.reload': 'Prendre sa version',
357 'workspace.overwrite': 'Garder la mienne',448 'workspace.overwrite': 'Garder la mienne',
358 'workspace.cannotOpen': "Ce fichier ne peut pas être ouvert ici : {error}",449 'workspace.cannotOpen': "Ce fichier ne peut pas être ouvert ici : {error}",
359- 'workspace.unavailable': "Les fichiers de {agent} ne peuvent pas être affichés : {problem}",450+ 'workspace.unavailable': 'Les fichiers de {agent} ne peuvent pas être affichés.',
360 'terminal.ended': 'Le shell est terminé.',451 'terminal.ended': 'Le shell est terminé.',
361 'terminal.restart': 'Ouvrir un nouveau shell',452 'terminal.restart': 'Ouvrir un nouveau shell',
362- 'terminal.error': "Impossible d'ouvrir un terminal : {error}",453+ 'terminal.error': "Impossible d'ouvrir un terminal.",
454+
455+ 'welcome.title': 'Bienvenue dans Sidekicks',
456+ 'welcome.tagline': "Discutez avec des agents d'IA dans des channels, comme avec des collègues.",
457+ 'welcome.step1.title': 'Ajoutez un agent',
458+ 'welcome.step1.body': 'Un assistant qui tourne sur votre ordinateur, dans Docker ou dans une sandbox sbx.',
459+ 'welcome.step2.title': 'Ouvrez un channel',
460+ 'welcome.step2.body': "#general est prêt. Créez-en d'autres, un par sujet.",
461+ 'welcome.step3.title': 'Appelez-le avec @',
462+ 'welcome.step3.body': '« @alice bonjour » : Alice rejoint le channel et vous répond.',
463+ 'welcome.addFirst': '+ Ajouter mon premier agent',
464+ 'welcome.openConfig': 'Ouvrir une configuration…',
465+ 'welcome.configAt': 'Configuration : {path}',
466+ 'welcome.pick': 'Choisissez un channel, ou cliquez sur un agent pour lui parler directement.',
467+ 'agents.add': '+ Ajouter un agent',
468+ 'palette.label': 'Palette de commandes',
469+ 'palette.placeholder': 'Aller à un channel ou un agent, ou lancer une action…',
470+ 'palette.none': 'Aucun résultat.',
471+ 'palette.channel': 'channel',
472+ 'palette.direct': 'conversation',
473+ 'palette.files': 'fichiers',
474+ 'palette.filesOf': 'Fichiers de {agent}',
475+ 'action.newChannel': 'Nouveau channel',
476+ 'action.addAgent': 'Ajouter un agent',
477+ 'action.focusComposer': 'Écrire un message',
478+ 'action.leftDock': 'Afficher ou masquer channels et agents',
479+ 'action.settings': 'Afficher ou masquer Settings',
480+ 'action.log': 'Afficher ou masquer le journal',
481+ 'action.openConfig': 'Ouvrir une configuration…',
482+ 'action.palette': 'Palette de commandes',
483+ 'assistant.title': 'Ajouter un agent',
484+ 'assistant.intro': "Sidekicks écrit l'agent dans votre configuration à votre place. Vous pourrez le modifier dans Settings.",
485+ 'assistant.who': 'Qui est-ce ?',
486+ 'assistant.agentTitle': 'Nom affiché',
487+ 'assistant.agentTitlePlaceholder': 'Alice (assistante)',
488+ 'assistant.name': 'Nom utilisé avec @',
489+ 'assistant.nameHint': 'Minuscules, chiffres, - et _ ; commence par une lettre.',
490+ 'assistant.icon': 'Icône',
491+ 'assistant.description': 'Ce qu\'il fait',
492+ 'assistant.descriptionPlaceholder': 'Répond aux questions simples',
493+ 'assistant.where': 'Où tourne-t-il ?',
494+ 'assistant.local': 'Sur cet ordinateur',
495+ 'assistant.container': 'Dans un conteneur Docker',
496+ 'assistant.sbx': 'Dans une sandbox sbx',
497+ 'assistant.containerName': 'Nom du conteneur',
498+ 'assistant.sandboxName': 'Nom de la sandbox',
499+ 'assistant.boxHint': "Elle doit être démarrée, avec le dossier de l'agent monté au même chemin.",
500+ 'assistant.how': 'Comment le démarrer ?',
501+ 'assistant.program': 'Programme',
502+ 'assistant.programPlaceholder': 'mm',
503+ 'assistant.programHint': "Le programme de l'agent, tel que vous le taperiez dans un terminal. Il doit parler ACP.",
504+ 'assistant.arguments': 'Arguments',
505+ 'assistant.argumentsPlaceholder': '-acp -config agent.yaml',
506+ 'assistant.workspace': "Dossier de l'agent",
507+ 'assistant.workspaceHint': "L'agent y travaille ; il est créé s'il n'existe pas.",
508+ 'assistant.choose': 'Choisir…',
509+ 'assistant.add': "Ajouter l'agent",
510+ 'assistant.cancel': 'Annuler',
511+ 'assistant.adding': 'Ajout…',
512+ 'assistant.missing.name': "Donnez un nom à l'agent.",
513+ 'assistant.missing.nameFormat': 'Le nom utilisé avec @ ne contient que des minuscules, des chiffres, - et _, et commence par une lettre.',
514+ 'assistant.missing.program': "Indiquez le programme qui démarre l'agent.",
515+ 'assistant.missing.workspace': "Choisissez le dossier de l'agent.",
516+ 'assistant.missing.box': 'Donnez le nom du conteneur ou de la sandbox.',
517+ 'assistant.refused': "L'agent n'a pas été ajouté :",
363 }518 }
added frontend/src/lib/layout/CommandPalette.svelte +102 -0
new file mode 100644
@@ -0,0 +1,102 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The command palette (⌘K, issue #35): go to a channel, an agent's
4+ // conversation or files, or run an action, without the mouse.
5+ // ↑↓ choose, Enter runs, Escape closes.
6+ import { t } from '../i18n/index.svelte'
7+ import { filterEntries, isMac, shortcutLabel, type Action, type PaletteEntry } from '../shortcuts'
8+ import { act, appState, openDirect, openWorkspace, selectChannel } from '../state.svelte'
9+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
10+
11+ let { run }: { run: (action: Action) => void } = $props()
12+
13+ let query = $state('')
14+ let selected = $state(0)
15+ let input: HTMLInputElement | undefined = $state()
16+ const mac = isMac()
17+
18+ const actions: Action[] = ['newChannel', 'addAgent', 'focusComposer', 'leftDock', 'settings', 'log']
19+
20+ const entries = $derived.by((): PaletteEntry[] => {
21+ return [
22+ ...appState.channels.filter((channel) => !channel.direct).map((channel) => ({
23+ id: `channel:${channel.id}`, label: `# ${channel.name}`, hint: t('palette.channel'), run: () => void selectChannel(channel.id),
24+ })),
25+ ...appState.agents.map((agent) => ({
26+ id: `agent:${agent.name}`, label: `${agent.icon ? agent.icon + ' ' : ''}${agent.title}`, hint: t('palette.direct'),
27+ run: () => void openDirect(agent.name),
28+ })),
29+ ...appState.agents.map((agent) => ({
30+ id: `files:${agent.name}`, label: t('palette.filesOf', { agent: agent.title }), hint: t('palette.files'), run: () => openWorkspace(agent.name),
31+ })),
32+ ...actions.map((action) => ({ id: `action:${action}`, label: t(`action.${action}`), hint: shortcutLabel(action, mac), run: () => run(action) })),
33+ { id: 'action:openConfig', label: t('action.openConfig'), hint: '', run: () => void act(() => ConfigService.Choose()) },
34+ ]
35+ })
36+ const found = $derived(filterEntries(query, entries))
37+
38+ $effect(() => {
39+ void query
40+ selected = 0
41+ })
42+ $effect(() => input?.focus())
43+
44+ function close() {
45+ appState.paletteOpen = false
46+ }
47+
48+ function choose(entry: PaletteEntry | undefined) {
49+ if (!entry) return
50+ close()
51+ entry.run()
52+ }
53+
54+ function onKeydown(event: KeyboardEvent) {
55+ if (event.key === 'Escape') close()
56+ else if (event.key === 'ArrowDown') selected = Math.min(selected + 1, found.length - 1)
57+ else if (event.key === 'ArrowUp') selected = Math.max(selected - 1, 0)
58+ else if (event.key === 'Enter') choose(found[selected])
59+ else return
60+ event.preventDefault()
61+ }
62+</script>
63+
64+<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
65+<div class="backdrop" onclick={close}>
66+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
67+ <div class="palette" role="dialog" aria-modal="true" aria-label={t('palette.label')} tabindex="-1" onclick={(event) => event.stopPropagation()}>
68+ <input
69+ bind:this={input}
70+ bind:value={query}
71+ onkeydown={onKeydown}
72+ placeholder={t('palette.placeholder')}
73+ role="combobox"
74+ aria-expanded="true"
75+ aria-controls="palette-list"
76+ aria-activedescendant={found[selected] ? `palette-${selected}` : undefined}
77+ spellcheck="false"
78+ />
79+ <ul id="palette-list" role="listbox">
80+ {#each found as entry, index (entry.id)}
81+ <li id="palette-{index}" role="option" aria-selected={index === selected} class:selected={index === selected}>
82+ <button tabindex="-1" onclick={() => choose(entry)} onmousemove={() => (selected = index)}>
83+ <span>{entry.label}</span><span class="hint">{entry.hint}</span>
84+ </button>
85+ </li>
86+ {:else}
87+ <li class="none">{t('palette.none')}</li>
88+ {/each}
89+ </ul>
90+ </div>
91+</div>
92+
93+<style>
94+ .backdrop { position: fixed; inset: 0; background: rgb(0 0 0 / 0.25); display: flex; justify-content: center; align-items: flex-start; padding-top: 12vh; z-index: 50; }
95+ .palette { width: min(560px, calc(100vw - 32px)); background: var(--panel); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 12px 40px rgb(0 0 0 / 0.25); overflow: hidden; }
96+ input { width: 100%; box-sizing: border-box; border: none; border-bottom: 1px solid var(--border); padding: 12px 14px; font: inherit; font-size: 15px; background: none; color: var(--text); outline: none; }
97+ ul { list-style: none; margin: 0; padding: 6px; max-height: 50vh; overflow-y: auto; }
98+ li button { width: 100%; display: flex; justify-content: space-between; gap: 12px; border: none; background: none; padding: 7px 10px; border-radius: 6px; text-align: left; color: var(--text); cursor: pointer; }
99+ li.selected button { background: var(--panel-2); outline: 1px solid var(--accent); }
100+ .hint { color: var(--muted); font-size: 12px; flex: none; }
101+ .none { color: var(--muted); padding: 8px 10px; }
102+</style>
new file mode 100644
@@ -0,0 +1,102 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The command palette (⌘K, issue #35): go to a channel, an agent's
4+ // conversation or files, or run an action, without the mouse.
5+ // ↑↓ choose, Enter runs, Escape closes.
6+ import { t } from '../i18n/index.svelte'
7+ import { filterEntries, isMac, shortcutLabel, type Action, type PaletteEntry } from '../shortcuts'
8+ import { act, appState, openDirect, openWorkspace, selectChannel } from '../state.svelte'
9+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
10+
11+ let { run }: { run: (action: Action) => void } = $props()
12+
13+ let query = $state('')
14+ let selected = $state(0)
15+ let input: HTMLInputElement | undefined = $state()
16+ const mac = isMac()
17+
18+ const actions: Action[] = ['newChannel', 'addAgent', 'focusComposer', 'leftDock', 'settings', 'log']
19+
20+ const entries = $derived.by((): PaletteEntry[] => {
21+ return [
22+ ...appState.channels.filter((channel) => !channel.direct).map((channel) => ({
23+ id: `channel:${channel.id}`, label: `# ${channel.name}`, hint: t('palette.channel'), run: () => void selectChannel(channel.id),
24+ })),
25+ ...appState.agents.map((agent) => ({
26+ id: `agent:${agent.name}`, label: `${agent.icon ? agent.icon + ' ' : ''}${agent.title}`, hint: t('palette.direct'),
27+ run: () => void openDirect(agent.name),
28+ })),
29+ ...appState.agents.map((agent) => ({
30+ id: `files:${agent.name}`, label: t('palette.filesOf', { agent: agent.title }), hint: t('palette.files'), run: () => openWorkspace(agent.name),
31+ })),
32+ ...actions.map((action) => ({ id: `action:${action}`, label: t(`action.${action}`), hint: shortcutLabel(action, mac), run: () => run(action) })),
33+ { id: 'action:openConfig', label: t('action.openConfig'), hint: '', run: () => void act(() => ConfigService.Choose()) },
34+ ]
35+ })
36+ const found = $derived(filterEntries(query, entries))
37+
38+ $effect(() => {
39+ void query
40+ selected = 0
41+ })
42+ $effect(() => input?.focus())
43+
44+ function close() {
45+ appState.paletteOpen = false
46+ }
47+
48+ function choose(entry: PaletteEntry | undefined) {
49+ if (!entry) return
50+ close()
51+ entry.run()
52+ }
53+
54+ function onKeydown(event: KeyboardEvent) {
55+ if (event.key === 'Escape') close()
56+ else if (event.key === 'ArrowDown') selected = Math.min(selected + 1, found.length - 1)
57+ else if (event.key === 'ArrowUp') selected = Math.max(selected - 1, 0)
58+ else if (event.key === 'Enter') choose(found[selected])
59+ else return
60+ event.preventDefault()
61+ }
62+</script>
63+
64+<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
65+<div class="backdrop" onclick={close}>
66+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
67+ <div class="palette" role="dialog" aria-modal="true" aria-label={t('palette.label')} tabindex="-1" onclick={(event) => event.stopPropagation()}>
68+ <input
69+ bind:this={input}
70+ bind:value={query}
71+ onkeydown={onKeydown}
72+ placeholder={t('palette.placeholder')}
73+ role="combobox"
74+ aria-expanded="true"
75+ aria-controls="palette-list"
76+ aria-activedescendant={found[selected] ? `palette-${selected}` : undefined}
77+ spellcheck="false"
78+ />
79+ <ul id="palette-list" role="listbox">
80+ {#each found as entry, index (entry.id)}
81+ <li id="palette-{index}" role="option" aria-selected={index === selected} class:selected={index === selected}>
82+ <button tabindex="-1" onclick={() => choose(entry)} onmousemove={() => (selected = index)}>
83+ <span>{entry.label}</span><span class="hint">{entry.hint}</span>
84+ </button>
85+ </li>
86+ {:else}
87+ <li class="none">{t('palette.none')}</li>
88+ {/each}
89+ </ul>
90+ </div>
91+</div>
92+
93+<style>
94+ .backdrop { position: fixed; inset: 0; background: rgb(0 0 0 / 0.25); display: flex; justify-content: center; align-items: flex-start; padding-top: 12vh; z-index: 50; }
95+ .palette { width: min(560px, calc(100vw - 32px)); background: var(--panel); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 12px 40px rgb(0 0 0 / 0.25); overflow: hidden; }
96+ input { width: 100%; box-sizing: border-box; border: none; border-bottom: 1px solid var(--border); padding: 12px 14px; font: inherit; font-size: 15px; background: none; color: var(--text); outline: none; }
97+ ul { list-style: none; margin: 0; padding: 6px; max-height: 50vh; overflow-y: auto; }
98+ li button { width: 100%; display: flex; justify-content: space-between; gap: 12px; border: none; background: none; padding: 7px 10px; border-radius: 6px; text-align: left; color: var(--text); cursor: pointer; }
99+ li.selected button { background: var(--panel-2); outline: 1px solid var(--accent); }
100+ .hint { color: var(--muted); font-size: 12px; flex: none; }
101+ .none { color: var(--muted); padding: 8px 10px; }
102+</style>
added frontend/src/lib/layout/Logo.svelte +21 -0
new file mode 100644
@@ -0,0 +1,21 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The Sidekicks mark, "Orbite": you in the centre, your agents in orbit.
4+ // Its colours come from the --logo-* tokens, so it follows the theme.
5+ // Usage: <Logo size={160} />
6+ let { size = 64 }: { size?: number } = $props()
7+ const id = $props.id()
8+</script>
9+
10+<svg width={size} height={size} viewBox="0 0 100 100" aria-hidden="true">
11+ <!-- The ring stops short of the two small dots. -->
12+ <mask id="cut-{id}">
13+ <rect width="100" height="100" fill="#fff" />
14+ <circle cx="76" cy="28" r="12" fill="#000" />
15+ <circle cx="18" cy="39" r="10" fill="#000" />
16+ </mask>
17+ <circle cx="50" cy="50" r="35" fill="none" stroke="var(--logo-ring)" stroke-width="4" mask="url(#cut-{id})" />
18+ <circle cx="50" cy="50" r="15" fill="var(--logo-centre)" />
19+ <circle cx="76" cy="28" r="9" fill="var(--logo-teal)" />
20+ <circle cx="18" cy="39" r="7" fill="var(--logo-coral)" />
21+</svg>
new file mode 100644
@@ -0,0 +1,21 @@
1+<!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2+<script lang="ts">
3+ // The Sidekicks mark, "Orbite": you in the centre, your agents in orbit.
4+ // Its colours come from the --logo-* tokens, so it follows the theme.
5+ // Usage: <Logo size={160} />
6+ let { size = 64 }: { size?: number } = $props()
7+ const id = $props.id()
8+</script>
9+
10+<svg width={size} height={size} viewBox="0 0 100 100" aria-hidden="true">
11+ <!-- The ring stops short of the two small dots. -->
12+ <mask id="cut-{id}">
13+ <rect width="100" height="100" fill="#fff" />
14+ <circle cx="76" cy="28" r="12" fill="#000" />
15+ <circle cx="18" cy="39" r="10" fill="#000" />
16+ </mask>
17+ <circle cx="50" cy="50" r="35" fill="none" stroke="var(--logo-ring)" stroke-width="4" mask="url(#cut-{id})" />
18+ <circle cx="50" cy="50" r="15" fill="var(--logo-centre)" />
19+ <circle cx="76" cy="28" r="9" fill="var(--logo-teal)" />
20+ <circle cx="18" cy="39" r="7" fill="var(--logo-coral)" />
21+</svg>
modified frontend/src/lib/layout/Shell.svelte +48 -0
@@ -5,6 +5,9 @@
55 import type { Registry } from '../panels/registry'
66 import Dock from './Dock.svelte'
77 import StatusBar from './StatusBar.svelte'
8+ import CommandPalette from './CommandPalette.svelte'
9+ import { actionFor, isMac, type Action } from '../shortcuts'
10+ import { appState, openAssistant } from '../state.svelte'
811
912 let { registry }: { registry: Registry } = $props()
1013
@@ -26,6 +29,46 @@
2629 bottom?.collapse()
2730 })
2831
32+ const mac = isMac()
33+
34+ // Issue #35: the shortcuts of lib/shortcuts.ts. Outside macOS they use
35+ // Ctrl, which the shell of the terminal needs (Ctrl+L, Ctrl+K…): there,
36+ // the terminal keeps them.
37+ function onKeydown(event: KeyboardEvent) {
38+ const action = actionFor(event, mac)
39+ if (!action) return
40+ if (!mac && event.target instanceof Element && event.target.closest('.xterm')) return
41+ event.preventDefault()
42+ run(action)
43+ }
44+
45+ function run(action: Action) {
46+ switch (action) {
47+ case 'palette':
48+ appState.paletteOpen = !appState.paletteOpen
49+ break
50+ case 'newChannel':
51+ if (collapsed.left) toggle('left')
52+ appState.newChannelRequest++
53+ break
54+ case 'addAgent':
55+ openAssistant()
56+ break
57+ case 'focusComposer':
58+ document.querySelector<HTMLTextAreaElement>('.composer textarea')?.focus()
59+ break
60+ case 'leftDock':
61+ toggle('left')
62+ break
63+ case 'settings':
64+ toggle('right')
65+ break
66+ case 'log':
67+ toggle('bottom')
68+ break
69+ }
70+ }
71+
2972 function toggle(dock: 'left' | 'right' | 'bottom') {
3073 const pane = { left, right, bottom }[dock]
3174 if (!pane) return
@@ -41,6 +84,8 @@
4184 <!-- Layout of issue #12 / wireframe 1: the conversation in the centre, channels
4285 and agents on the left, Settings on the right and the logs at the bottom
4386 folded at every launch. autoSaveId keeps the sizes from one launch to the next. -->
87+<svelte:window onkeydown={onKeydown} />
88+
4489 <div class="shell">
4590 <div class="main">
4691 <PaneGroup direction="horizontal" autoSaveId="sidekicks-shell">
@@ -70,6 +115,9 @@
70115 </div>
71116 <StatusBar {collapsed} onToggle={toggle} />
72117 </div>
118+{#if appState.paletteOpen}
119+ <CommandPalette {run} />
120+{/if}
73121
74122 <style>
75123 .shell { height: 100%; display: flex; flex-direction: column; }
@@ -5,6 +5,9 @@
5 import type { Registry } from '../panels/registry'5 import type { Registry } from '../panels/registry'
6 import Dock from './Dock.svelte'6 import Dock from './Dock.svelte'
7 import StatusBar from './StatusBar.svelte'7 import StatusBar from './StatusBar.svelte'
8+ import CommandPalette from './CommandPalette.svelte'
9+ import { actionFor, isMac, type Action } from '../shortcuts'
10+ import { appState, openAssistant } from '../state.svelte'
8 11
9 let { registry }: { registry: Registry } = $props()12 let { registry }: { registry: Registry } = $props()
10 13
@@ -26,6 +29,46 @@
26 bottom?.collapse()29 bottom?.collapse()
27 })30 })
28 31
32+ const mac = isMac()
33+
34+ // Issue #35: the shortcuts of lib/shortcuts.ts. Outside macOS they use
35+ // Ctrl, which the shell of the terminal needs (Ctrl+L, Ctrl+K…): there,
36+ // the terminal keeps them.
37+ function onKeydown(event: KeyboardEvent) {
38+ const action = actionFor(event, mac)
39+ if (!action) return
40+ if (!mac && event.target instanceof Element && event.target.closest('.xterm')) return
41+ event.preventDefault()
42+ run(action)
43+ }
44+
45+ function run(action: Action) {
46+ switch (action) {
47+ case 'palette':
48+ appState.paletteOpen = !appState.paletteOpen
49+ break
50+ case 'newChannel':
51+ if (collapsed.left) toggle('left')
52+ appState.newChannelRequest++
53+ break
54+ case 'addAgent':
55+ openAssistant()
56+ break
57+ case 'focusComposer':
58+ document.querySelector<HTMLTextAreaElement>('.composer textarea')?.focus()
59+ break
60+ case 'leftDock':
61+ toggle('left')
62+ break
63+ case 'settings':
64+ toggle('right')
65+ break
66+ case 'log':
67+ toggle('bottom')
68+ break
69+ }
70+ }
71+
29 function toggle(dock: 'left' | 'right' | 'bottom') {72 function toggle(dock: 'left' | 'right' | 'bottom') {
30 const pane = { left, right, bottom }[dock]73 const pane = { left, right, bottom }[dock]
31 if (!pane) return74 if (!pane) return
@@ -41,6 +84,8 @@
41 <!-- Layout of issue #12 / wireframe 1: the conversation in the centre, channels84 <!-- Layout of issue #12 / wireframe 1: the conversation in the centre, channels
42 and agents on the left, Settings on the right and the logs at the bottom85 and agents on the left, Settings on the right and the logs at the bottom
43 folded at every launch. autoSaveId keeps the sizes from one launch to the next. -->86 folded at every launch. autoSaveId keeps the sizes from one launch to the next. -->
87+<svelte:window onkeydown={onKeydown} />
88+
44 <div class="shell">89 <div class="shell">
45 <div class="main">90 <div class="main">
46 <PaneGroup direction="horizontal" autoSaveId="sidekicks-shell">91 <PaneGroup direction="horizontal" autoSaveId="sidekicks-shell">
@@ -70,6 +115,9 @@
70 </div>115 </div>
71 <StatusBar {collapsed} onToggle={toggle} />116 <StatusBar {collapsed} onToggle={toggle} />
72 </div>117 </div>
118+{#if appState.paletteOpen}
119+ <CommandPalette {run} />
120+{/if}
73 121
74 <style>122 <style>
75 .shell { height: 100%; display: flex; flex-direction: column; }123 .shell { height: 100%; display: flex; flex-direction: column; }
modified frontend/src/lib/panels/AgentsPanel.svelte +6 -2
@@ -2,8 +2,9 @@
22 <script lang="ts">
33 import { hasMessage, t, type MessageKey } from '../i18n/index.svelte'
44 import ActionError from '../chat/ActionError.svelte'
5+ import ErrorText from '../chat/ErrorText.svelte'
56 import { AgentService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
6- import { act, appState, channelOf, openDirect, openWorkspace } from '../state.svelte'
7+ import { act, appState, channelOf, openAssistant, openDirect, openWorkspace } from '../state.svelte'
78
89 // The agent of the direct conversation shown, to highlight it.
910 const directAgent = $derived(channelOf(appState.selectedChannel)?.direct ?? '')
@@ -28,12 +29,13 @@
2829 {/if}
2930 </span>
3031 {#if agent.error}
31- <p class="failure">{agent.error}</p>
32+ <div class="failure"><ErrorText raw={agent.error} /></div>
3233 {/if}
3334 </li>
3435 {/each}
3536 </ul>
3637 {/if}
38+<button class="add" onclick={openAssistant}>{t('agents.add')}</button>
3739 <ActionError />
3840
3941 <style>
@@ -55,4 +57,6 @@
5557 .actions button { padding: 1px 7px; font-size: 11px; }
5658 .failure { grid-column: 2 / span 2; grid-row: 3; margin: 2px 0 0; color: var(--danger); font-size: 12px; white-space: pre-wrap; }
5759 .empty { color: var(--muted); padding: 0 8px; }
60+ .add { margin: 6px 8px; border: 1px dashed var(--border); background: none; color: var(--muted); font-size: 12px; }
61+ .add:hover { color: var(--text); border-color: var(--accent); }
5862 </style>
@@ -2,8 +2,9 @@
2 <script lang="ts">2 <script lang="ts">
3 import { hasMessage, t, type MessageKey } from '../i18n/index.svelte'3 import { hasMessage, t, type MessageKey } from '../i18n/index.svelte'
4 import ActionError from '../chat/ActionError.svelte'4 import ActionError from '../chat/ActionError.svelte'
5+ import ErrorText from '../chat/ErrorText.svelte'
5 import { AgentService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'6 import { AgentService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
6- import { act, appState, channelOf, openDirect, openWorkspace } from '../state.svelte'7+ import { act, appState, channelOf, openAssistant, openDirect, openWorkspace } from '../state.svelte'
7 8
8 // The agent of the direct conversation shown, to highlight it.9 // The agent of the direct conversation shown, to highlight it.
9 const directAgent = $derived(channelOf(appState.selectedChannel)?.direct ?? '')10 const directAgent = $derived(channelOf(appState.selectedChannel)?.direct ?? '')
@@ -28,12 +29,13 @@
28 {/if}29 {/if}
29 </span>30 </span>
30 {#if agent.error}31 {#if agent.error}
31- <p class="failure">{agent.error}</p>32+ <div class="failure"><ErrorText raw={agent.error} /></div>
32 {/if}33 {/if}
33 </li>34 </li>
34 {/each}35 {/each}
35 </ul>36 </ul>
36 {/if}37 {/if}
38+<button class="add" onclick={openAssistant}>{t('agents.add')}</button>
37 <ActionError />39 <ActionError />
38 40
39 <style>41 <style>
@@ -55,4 +57,6 @@
55 .actions button { padding: 1px 7px; font-size: 11px; }57 .actions button { padding: 1px 7px; font-size: 11px; }
56 .failure { grid-column: 2 / span 2; grid-row: 3; margin: 2px 0 0; color: var(--danger); font-size: 12px; white-space: pre-wrap; }58 .failure { grid-column: 2 / span 2; grid-row: 3; margin: 2px 0 0; color: var(--danger); font-size: 12px; white-space: pre-wrap; }
57 .empty { color: var(--muted); padding: 0 8px; }59 .empty { color: var(--muted); padding: 0 8px; }
60+ .add { margin: 6px 8px; border: 1px dashed var(--border); background: none; color: var(--muted); font-size: 12px; }
61+ .add:hover { color: var(--text); border-color: var(--accent); }
58 </style>62 </style>
modified frontend/src/lib/panels/ChannelsPanel.svelte +8 -0
@@ -13,6 +13,14 @@
1313 const directs = $derived(visibleDirects(appState.channels, appState.agents.map((agent) => agent.name)))
1414
1515 let creating = $state(false)
16+ // ⌘N, or "New channel" in the palette.
17+ let lastRequest = appState.newChannelRequest
18+ $effect(() => {
19+ if (appState.newChannelRequest === lastRequest) return
20+ lastRequest = appState.newChannelRequest
21+ appState.actionError = ''
22+ creating = true
23+ })
1624 let newName = $state('')
1725 // The channel being renamed, and the one waiting for a delete confirmation.
1826 let renaming = $state('')
@@ -13,6 +13,14 @@
13 const directs = $derived(visibleDirects(appState.channels, appState.agents.map((agent) => agent.name)))13 const directs = $derived(visibleDirects(appState.channels, appState.agents.map((agent) => agent.name)))
14 14
15 let creating = $state(false)15 let creating = $state(false)
16+ // ⌘N, or "New channel" in the palette.
17+ let lastRequest = appState.newChannelRequest
18+ $effect(() => {
19+ if (appState.newChannelRequest === lastRequest) return
20+ lastRequest = appState.newChannelRequest
21+ appState.actionError = ''
22+ creating = true
23+ })
16 let newName = $state('')24 let newName = $state('')
17 // The channel being renamed, and the one waiting for a delete confirmation.25 // The channel being renamed, and the one waiting for a delete confirmation.
18 let renaming = $state('')26 let renaming = $state('')
modified frontend/src/lib/panels/ConversationPanel.svelte +40 -8
@@ -1,16 +1,19 @@
11 <!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
22 <script lang="ts">
3- import { t } from '../i18n/index.svelte'
3+ import { t, type MessageKey } from '../i18n/index.svelte'
44 import { tick } from 'svelte'
55 import Composer from '../chat/Composer.svelte'
66 import MessageView from '../chat/MessageView.svelte'
77 import PermissionCard from '../chat/PermissionCard.svelte'
88 import Spinner from '../chat/Spinner.svelte'
99 import AgentView from '../agent/AgentView.svelte'
10+ import AddAgentForm from '../agent/AddAgentForm.svelte'
1011 import ActionError from '../chat/ActionError.svelte'
12+ import Logo from '../layout/Logo.svelte'
1113 import { isNearBottom, type Trigger } from '../composer'
1214 import { usagePercent } from '../conversation'
13- import { addMember, agentTitle, appState, cancel, channelOf, loadOlder, prepareCommands, removeMember, send } from '../state.svelte'
15+ import { act, addMember, agentTitle, appState, cancel, channelOf, loadOlder, openAssistant, prepareCommands, removeMember, send } from '../state.svelte'
16+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
1417 import { commandSuggestions, isCommandPosition, mentionSuggestions, type AgentInfo, type Suggestions } from '../suggestions'
1518 import { stuckAfter, trailingFailures } from '../tools'
1619
@@ -113,7 +116,9 @@
113116 <div class="banner warning" role="alert">{t('conversation.historyProblem', { problem: appState.config.historyProblem })}</div>
114117 {/if}
115118
116- {#if appState.workspaceAgent}
119+ {#if appState.assistant}
120+ <AddAgentForm />
121+ {:else if appState.workspaceAgent}
117122 {#key appState.workspaceAgent}
118123 <AgentView agent={appState.workspaceAgent} />
119124 {/key}
@@ -199,11 +204,29 @@
199204 </div>
200205 {:else}
201206 <div class="welcome">
202- <h1>Sidekicks</h1>
207+ <Logo size={appState.agents.length === 0 ? 120 : 160} />
203208 {#if appState.agents.length === 0}
204- <p>{t('conversation.noAgents')}</p>
209+ <h1>{t('welcome.title')}</h1>
210+ <p class="tagline">{t('welcome.tagline')}</p>
211+ <ol class="steps">
212+ {#each [1, 2, 3] as step (step)}
213+ <li>
214+ <span class="number" aria-hidden="true">{step}</span>
215+ <span><strong>{t(`welcome.step${step}.title` as MessageKey)}</strong><br />{t(`welcome.step${step}.body` as MessageKey)}</span>
216+ </li>
217+ {/each}
218+ </ol>
219+ <div class="buttons">
220+ <button class="primary" onclick={openAssistant}>{t('welcome.addFirst')}</button>
221+ <button onclick={() => act(() => ConfigService.Choose())}>{t('welcome.openConfig')}</button>
222+ </div>
223+ {#if appState.config?.path}
224+ <p class="path">{t('welcome.configAt', { path: appState.config.path })}</p>
225+ {/if}
226+ <ActionError />
205227 {:else}
206- <p>{t('conversation.pickAgent')}</p>
228+ <h1>Sidekicks</h1>
229+ <p>{t('welcome.pick')}</p>
207230 {/if}
208231 </div>
209232 {/if}
@@ -229,6 +252,15 @@
229252 .stuck { display: flex; align-items: center; gap: 10px; margin: 0 18px 8px; padding: 8px 12px; border: 1px solid var(--warn); border-radius: var(--radius); color: var(--warn); font-size: 13px; }
230253 .stuck button { margin-left: auto; color: var(--danger); border-color: var(--danger); }
231254 .composer { padding: 0 18px 14px; }
232- .welcome { margin: auto; max-width: 460px; text-align: center; color: var(--muted); padding: 24px; }
233- h1 { color: var(--text); font-size: 22px; margin: 0 0 8px; }
255+ .welcome { margin: auto; max-width: 560px; text-align: center; color: var(--muted); padding: 24px; overflow-y: auto; }
256+ .tagline { margin: 0 0 20px; }
257+ .steps { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 10px; text-align: left; }
258+ .steps li { display: flex; align-items: center; gap: 14px; padding: 12px 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--panel); color: var(--muted); }
259+ .steps strong { color: var(--text); }
260+ .number { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-weight: 700; display: grid; place-items: center; }
261+ .buttons { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; }
262+ .buttons button { padding: 8px 16px; }
263+ .primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
264+ .path { font-size: 12px; margin-top: 16px; word-break: break-all; }
265+ h1 { color: var(--text); font-size: 22px; margin: 16px 0 8px; }
234266 </style>
@@ -1,16 +1,19 @@
1 <!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->1 <!-- Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE -->
2 <script lang="ts">2 <script lang="ts">
3- import { t } from '../i18n/index.svelte'3+ import { t, type MessageKey } from '../i18n/index.svelte'
4 import { tick } from 'svelte'4 import { tick } from 'svelte'
5 import Composer from '../chat/Composer.svelte'5 import Composer from '../chat/Composer.svelte'
6 import MessageView from '../chat/MessageView.svelte'6 import MessageView from '../chat/MessageView.svelte'
7 import PermissionCard from '../chat/PermissionCard.svelte'7 import PermissionCard from '../chat/PermissionCard.svelte'
8 import Spinner from '../chat/Spinner.svelte'8 import Spinner from '../chat/Spinner.svelte'
9 import AgentView from '../agent/AgentView.svelte'9 import AgentView from '../agent/AgentView.svelte'
10+ import AddAgentForm from '../agent/AddAgentForm.svelte'
10 import ActionError from '../chat/ActionError.svelte'11 import ActionError from '../chat/ActionError.svelte'
12+ import Logo from '../layout/Logo.svelte'
11 import { isNearBottom, type Trigger } from '../composer'13 import { isNearBottom, type Trigger } from '../composer'
12 import { usagePercent } from '../conversation'14 import { usagePercent } from '../conversation'
13- import { addMember, agentTitle, appState, cancel, channelOf, loadOlder, prepareCommands, removeMember, send } from '../state.svelte'15+ import { act, addMember, agentTitle, appState, cancel, channelOf, loadOlder, openAssistant, prepareCommands, removeMember, send } from '../state.svelte'
16+ import { ConfigService } from '../../../bindings/git.rickub.com/bots-garden/sidekicks/internal/app'
14 import { commandSuggestions, isCommandPosition, mentionSuggestions, type AgentInfo, type Suggestions } from '../suggestions'17 import { commandSuggestions, isCommandPosition, mentionSuggestions, type AgentInfo, type Suggestions } from '../suggestions'
15 import { stuckAfter, trailingFailures } from '../tools'18 import { stuckAfter, trailingFailures } from '../tools'
16 19
@@ -113,7 +116,9 @@
113 <div class="banner warning" role="alert">{t('conversation.historyProblem', { problem: appState.config.historyProblem })}</div>116 <div class="banner warning" role="alert">{t('conversation.historyProblem', { problem: appState.config.historyProblem })}</div>
114 {/if}117 {/if}
115 118
116- {#if appState.workspaceAgent}119+ {#if appState.assistant}
120+ <AddAgentForm />
121+ {:else if appState.workspaceAgent}
117 {#key appState.workspaceAgent}122 {#key appState.workspaceAgent}
118 <AgentView agent={appState.workspaceAgent} />123 <AgentView agent={appState.workspaceAgent} />
119 {/key}124 {/key}
@@ -199,11 +204,29 @@
199 </div>204 </div>
200 {:else}205 {:else}
201 <div class="welcome">206 <div class="welcome">
202- <h1>Sidekicks</h1>207+ <Logo size={appState.agents.length === 0 ? 120 : 160} />
203 {#if appState.agents.length === 0}208 {#if appState.agents.length === 0}
204- <p>{t('conversation.noAgents')}</p>209+ <h1>{t('welcome.title')}</h1>
210+ <p class="tagline">{t('welcome.tagline')}</p>
211+ <ol class="steps">
212+ {#each [1, 2, 3] as step (step)}
213+ <li>
214+ <span class="number" aria-hidden="true">{step}</span>
215+ <span><strong>{t(`welcome.step${step}.title` as MessageKey)}</strong><br />{t(`welcome.step${step}.body` as MessageKey)}</span>
216+ </li>
217+ {/each}
218+ </ol>
219+ <div class="buttons">
220+ <button class="primary" onclick={openAssistant}>{t('welcome.addFirst')}</button>
221+ <button onclick={() => act(() => ConfigService.Choose())}>{t('welcome.openConfig')}</button>
222+ </div>
223+ {#if appState.config?.path}
224+ <p class="path">{t('welcome.configAt', { path: appState.config.path })}</p>
225+ {/if}
226+ <ActionError />
205 {:else}227 {:else}
206- <p>{t('conversation.pickAgent')}</p>228+ <h1>Sidekicks</h1>
229+ <p>{t('welcome.pick')}</p>
207 {/if}230 {/if}
208 </div>231 </div>
209 {/if}232 {/if}
@@ -229,6 +252,15 @@
229 .stuck { display: flex; align-items: center; gap: 10px; margin: 0 18px 8px; padding: 8px 12px; border: 1px solid var(--warn); border-radius: var(--radius); color: var(--warn); font-size: 13px; }252 .stuck { display: flex; align-items: center; gap: 10px; margin: 0 18px 8px; padding: 8px 12px; border: 1px solid var(--warn); border-radius: var(--radius); color: var(--warn); font-size: 13px; }
230 .stuck button { margin-left: auto; color: var(--danger); border-color: var(--danger); }253 .stuck button { margin-left: auto; color: var(--danger); border-color: var(--danger); }
231 .composer { padding: 0 18px 14px; }254 .composer { padding: 0 18px 14px; }
232- .welcome { margin: auto; max-width: 460px; text-align: center; color: var(--muted); padding: 24px; }255+ .welcome { margin: auto; max-width: 560px; text-align: center; color: var(--muted); padding: 24px; overflow-y: auto; }
233- h1 { color: var(--text); font-size: 22px; margin: 0 0 8px; }256+ .tagline { margin: 0 0 20px; }
257+ .steps { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 10px; text-align: left; }
258+ .steps li { display: flex; align-items: center; gap: 14px; padding: 12px 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--panel); color: var(--muted); }
259+ .steps strong { color: var(--text); }
260+ .number { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-weight: 700; display: grid; place-items: center; }
261+ .buttons { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; }
262+ .buttons button { padding: 8px 16px; }
263+ .primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
264+ .path { font-size: 12px; margin-top: 16px; word-break: break-all; }
265+ h1 { color: var(--text); font-size: 22px; margin: 16px 0 8px; }
234 </style>266 </style>
added frontend/src/lib/shortcuts.test.ts +60 -0
new file mode 100644
@@ -0,0 +1,60 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { actionFor, filterEntries, isMac, shortcutLabel, shortcuts, type PaletteEntry } from './shortcuts'
5+
6+const press = (key: string, modifiers: Partial<{ metaKey: boolean; ctrlKey: boolean; shiftKey: boolean; altKey: boolean }> = {}) => ({
7+ key, metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, ...modifiers,
8+})
9+
10+describe('actionFor', () => {
11+ it('uses ⌘ on macOS and Ctrl elsewhere', () => {
12+ expect(actionFor(press('k', { metaKey: true }), true)).toBe('palette')
13+ expect(actionFor(press('k', { ctrlKey: true }), true)).toBeNull()
14+ expect(actionFor(press('k', { ctrlKey: true }), false)).toBe('palette')
15+ expect(actionFor(press('k', { metaKey: true }), false)).toBeNull()
16+ })
17+ it('tells Shift apart and ignores Alt and plain keys', () => {
18+ expect(actionFor(press('n', { metaKey: true }), true)).toBe('newChannel')
19+ expect(actionFor(press('N', { metaKey: true, shiftKey: true }), true)).toBe('addAgent')
20+ expect(actionFor(press('n', { metaKey: true, altKey: true }), true)).toBeNull()
21+ expect(actionFor(press('n'), true)).toBeNull()
22+ expect(actionFor(press(',', { metaKey: true }), true)).toBe('settings')
23+ })
24+ it('has one shortcut per action and key', () => {
25+ const keys = shortcuts.map((shortcut) => `${shortcut.shift ? 'shift+' : ''}${shortcut.key}`)
26+ expect(new Set(keys).size).toBe(keys.length)
27+ expect(new Set(shortcuts.map((shortcut) => shortcut.action)).size).toBe(shortcuts.length)
28+ })
29+})
30+
31+describe('shortcutLabel', () => {
32+ it('writes the shortcut for each platform', () => {
33+ expect(shortcutLabel('palette', true)).toBe('⌘K')
34+ expect(shortcutLabel('addAgent', true)).toBe('⇧⌘N')
35+ expect(shortcutLabel('settings', false)).toBe('Ctrl+,')
36+ expect(shortcutLabel('addAgent', false)).toBe('Ctrl+Shift+N')
37+ })
38+})
39+
40+describe('isMac', () => {
41+ it('reads the platform', () => {
42+ expect(isMac('MacIntel')).toBe(true)
43+ expect(isMac('Linux x86_64')).toBe(false)
44+ expect(isMac('Win32')).toBe(false)
45+ })
46+})
47+
48+describe('filterEntries', () => {
49+ const entry = (label: string): PaletteEntry => ({ id: label, label, hint: '', run: () => {} })
50+ const entries = [entry('# general'), entry('# translations'), entry('🐮 Garfield (translator)'), entry('New channel')]
51+ it('keeps every entry for an empty query', () => {
52+ expect(filterEntries('', entries)).toHaveLength(4)
53+ })
54+ it('matches without the # or the icon, best first', () => {
55+ expect(filterEntries('#gen', entries)[0].label).toBe('# general')
56+ expect(filterEntries('xyz', entries)).toEqual([])
57+ expect(filterEntries('gar', entries)[0].label).toBe('🐮 Garfield (translator)')
58+ expect(filterEntries('trans', entries).map((found) => found.label)).toEqual(['# translations', '🐮 Garfield (translator)'])
59+ })
60+})
new file mode 100644
@@ -0,0 +1,60 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+import { describe, expect, it } from 'vitest'
4+import { actionFor, filterEntries, isMac, shortcutLabel, shortcuts, type PaletteEntry } from './shortcuts'
5+
6+const press = (key: string, modifiers: Partial<{ metaKey: boolean; ctrlKey: boolean; shiftKey: boolean; altKey: boolean }> = {}) => ({
7+ key, metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, ...modifiers,
8+})
9+
10+describe('actionFor', () => {
11+ it('uses ⌘ on macOS and Ctrl elsewhere', () => {
12+ expect(actionFor(press('k', { metaKey: true }), true)).toBe('palette')
13+ expect(actionFor(press('k', { ctrlKey: true }), true)).toBeNull()
14+ expect(actionFor(press('k', { ctrlKey: true }), false)).toBe('palette')
15+ expect(actionFor(press('k', { metaKey: true }), false)).toBeNull()
16+ })
17+ it('tells Shift apart and ignores Alt and plain keys', () => {
18+ expect(actionFor(press('n', { metaKey: true }), true)).toBe('newChannel')
19+ expect(actionFor(press('N', { metaKey: true, shiftKey: true }), true)).toBe('addAgent')
20+ expect(actionFor(press('n', { metaKey: true, altKey: true }), true)).toBeNull()
21+ expect(actionFor(press('n'), true)).toBeNull()
22+ expect(actionFor(press(',', { metaKey: true }), true)).toBe('settings')
23+ })
24+ it('has one shortcut per action and key', () => {
25+ const keys = shortcuts.map((shortcut) => `${shortcut.shift ? 'shift+' : ''}${shortcut.key}`)
26+ expect(new Set(keys).size).toBe(keys.length)
27+ expect(new Set(shortcuts.map((shortcut) => shortcut.action)).size).toBe(shortcuts.length)
28+ })
29+})
30+
31+describe('shortcutLabel', () => {
32+ it('writes the shortcut for each platform', () => {
33+ expect(shortcutLabel('palette', true)).toBe('⌘K')
34+ expect(shortcutLabel('addAgent', true)).toBe('⇧⌘N')
35+ expect(shortcutLabel('settings', false)).toBe('Ctrl+,')
36+ expect(shortcutLabel('addAgent', false)).toBe('Ctrl+Shift+N')
37+ })
38+})
39+
40+describe('isMac', () => {
41+ it('reads the platform', () => {
42+ expect(isMac('MacIntel')).toBe(true)
43+ expect(isMac('Linux x86_64')).toBe(false)
44+ expect(isMac('Win32')).toBe(false)
45+ })
46+})
47+
48+describe('filterEntries', () => {
49+ const entry = (label: string): PaletteEntry => ({ id: label, label, hint: '', run: () => {} })
50+ const entries = [entry('# general'), entry('# translations'), entry('🐮 Garfield (translator)'), entry('New channel')]
51+ it('keeps every entry for an empty query', () => {
52+ expect(filterEntries('', entries)).toHaveLength(4)
53+ })
54+ it('matches without the # or the icon, best first', () => {
55+ expect(filterEntries('#gen', entries)[0].label).toBe('# general')
56+ expect(filterEntries('xyz', entries)).toEqual([])
57+ expect(filterEntries('gar', entries)[0].label).toBe('🐮 Garfield (translator)')
58+ expect(filterEntries('trans', entries).map((found) => found.label)).toEqual(['# translations', '🐮 Garfield (translator)'])
59+ })
60+})
added frontend/src/lib/shortcuts.ts +91 -0
new file mode 100644
@@ -0,0 +1,91 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Keyboard shortcuts (issue #35). ⌘ on macOS, Ctrl elsewhere. The table is
4+// the single source: the shell dispatches with actionFor, the palette and
5+// the reference documentation show shortcutLabel.
6+
7+import { matchScore } from './suggestions'
8+
9+export type Action = 'palette' | 'newChannel' | 'addAgent' | 'focusComposer' | 'leftDock' | 'settings' | 'log'
10+
11+export interface Shortcut {
12+ action: Action
13+ /** The key, lowercase, as KeyboardEvent.key gives it. */
14+ key: string
15+ shift?: boolean
16+}
17+
18+export const shortcuts: Shortcut[] = [
19+ { action: 'palette', key: 'k' },
20+ { action: 'newChannel', key: 'n' },
21+ { action: 'addAgent', key: 'n', shift: true },
22+ { action: 'focusComposer', key: 'l' },
23+ { action: 'leftDock', key: 'b' },
24+ { action: 'settings', key: ',' },
25+ { action: 'log', key: 'j' },
26+]
27+
28+export interface KeyLike {
29+ key: string
30+ metaKey: boolean
31+ ctrlKey: boolean
32+ shiftKey: boolean
33+ altKey: boolean
34+}
35+
36+/** Whether the computer is a Mac, where shortcuts use ⌘. */
37+export function isMac(platform = typeof navigator === 'undefined' ? '' : navigator.platform): boolean {
38+ return /mac/i.test(platform)
39+}
40+
41+/**
42+ * The action of a key press, or null.
43+ *
44+ * @example
45+ * actionFor({ key: 'k', metaKey: true, ctrlKey: false, shiftKey: false, altKey: false }, true) // 'palette'
46+ */
47+export function actionFor(event: KeyLike, mac: boolean): Action | null {
48+ const primary = mac ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey
49+ if (!primary || event.altKey) return null
50+ const key = event.key.toLowerCase()
51+ return shortcuts.find((shortcut) => shortcut.key === key && !!shortcut.shift === event.shiftKey)?.action ?? null
52+}
53+
54+/**
55+ * How the shortcut of action is written.
56+ *
57+ * @example
58+ * shortcutLabel('addAgent', true) // '⇧⌘N'
59+ * shortcutLabel('addAgent', false) // 'Ctrl+Shift+N'
60+ */
61+export function shortcutLabel(action: Action, mac: boolean): string {
62+ const shortcut = shortcuts.find((candidate) => candidate.action === action)
63+ if (!shortcut) return ''
64+ const key = shortcut.key.toUpperCase()
65+ if (mac) return `${shortcut.shift ? '⇧' : ''}⌘${key}`
66+ return `Ctrl+${shortcut.shift ? 'Shift+' : ''}${key}`
67+}
68+
69+/** An entry of the command palette. */
70+export interface PaletteEntry {
71+ id: string
72+ label: string
73+ /** Shown on the right: a shortcut, or the kind of entry. */
74+ hint: string
75+ run: () => void
76+}
77+
78+/**
79+ * The entries matching query, best first; all of them when query is empty.
80+ *
81+ * @example
82+ * filterEntries('gen', entries) // [{ label: '# general', … }]
83+ */
84+export function filterEntries(query: string, entries: PaletteEntry[]): PaletteEntry[] {
85+ const q = query.trim().replace(/^#/, '')
86+ return entries
87+ .map((entry, index) => ({ entry, index, score: matchScore(q, entry.label.replace(/^[#\p{Extended_Pictographic}\s]+/u, '')) }))
88+ .filter((candidate) => candidate.score !== null)
89+ .sort((a, b) => a.score! - b.score! || a.index - b.index)
90+ .map((candidate) => candidate.entry)
91+}
new file mode 100644
@@ -0,0 +1,91 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Keyboard shortcuts (issue #35). ⌘ on macOS, Ctrl elsewhere. The table is
4+// the single source: the shell dispatches with actionFor, the palette and
5+// the reference documentation show shortcutLabel.
6+
7+import { matchScore } from './suggestions'
8+
9+export type Action = 'palette' | 'newChannel' | 'addAgent' | 'focusComposer' | 'leftDock' | 'settings' | 'log'
10+
11+export interface Shortcut {
12+ action: Action
13+ /** The key, lowercase, as KeyboardEvent.key gives it. */
14+ key: string
15+ shift?: boolean
16+}
17+
18+export const shortcuts: Shortcut[] = [
19+ { action: 'palette', key: 'k' },
20+ { action: 'newChannel', key: 'n' },
21+ { action: 'addAgent', key: 'n', shift: true },
22+ { action: 'focusComposer', key: 'l' },
23+ { action: 'leftDock', key: 'b' },
24+ { action: 'settings', key: ',' },
25+ { action: 'log', key: 'j' },
26+]
27+
28+export interface KeyLike {
29+ key: string
30+ metaKey: boolean
31+ ctrlKey: boolean
32+ shiftKey: boolean
33+ altKey: boolean
34+}
35+
36+/** Whether the computer is a Mac, where shortcuts use ⌘. */
37+export function isMac(platform = typeof navigator === 'undefined' ? '' : navigator.platform): boolean {
38+ return /mac/i.test(platform)
39+}
40+
41+/**
42+ * The action of a key press, or null.
43+ *
44+ * @example
45+ * actionFor({ key: 'k', metaKey: true, ctrlKey: false, shiftKey: false, altKey: false }, true) // 'palette'
46+ */
47+export function actionFor(event: KeyLike, mac: boolean): Action | null {
48+ const primary = mac ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey
49+ if (!primary || event.altKey) return null
50+ const key = event.key.toLowerCase()
51+ return shortcuts.find((shortcut) => shortcut.key === key && !!shortcut.shift === event.shiftKey)?.action ?? null
52+}
53+
54+/**
55+ * How the shortcut of action is written.
56+ *
57+ * @example
58+ * shortcutLabel('addAgent', true) // '⇧⌘N'
59+ * shortcutLabel('addAgent', false) // 'Ctrl+Shift+N'
60+ */
61+export function shortcutLabel(action: Action, mac: boolean): string {
62+ const shortcut = shortcuts.find((candidate) => candidate.action === action)
63+ if (!shortcut) return ''
64+ const key = shortcut.key.toUpperCase()
65+ if (mac) return `${shortcut.shift ? '⇧' : ''}⌘${key}`
66+ return `Ctrl+${shortcut.shift ? 'Shift+' : ''}${key}`
67+}
68+
69+/** An entry of the command palette. */
70+export interface PaletteEntry {
71+ id: string
72+ label: string
73+ /** Shown on the right: a shortcut, or the kind of entry. */
74+ hint: string
75+ run: () => void
76+}
77+
78+/**
79+ * The entries matching query, best first; all of them when query is empty.
80+ *
81+ * @example
82+ * filterEntries('gen', entries) // [{ label: '# general', … }]
83+ */
84+export function filterEntries(query: string, entries: PaletteEntry[]): PaletteEntry[] {
85+ const q = query.trim().replace(/^#/, '')
86+ return entries
87+ .map((entry, index) => ({ entry, index, score: matchScore(q, entry.label.replace(/^[#\p{Extended_Pictographic}\s]+/u, '')) }))
88+ .filter((candidate) => candidate.score !== null)
89+ .sort((a, b) => a.score! - b.score! || a.index - b.index)
90+ .map((candidate) => candidate.entry)
91+}
modified frontend/src/lib/state.svelte.ts +31 -0
@@ -71,6 +71,14 @@ export const appState = $state({
7171 workspaceRevision: {} as Record<string, number>,
7272 /** The last error returned by an action, shown to the user. */
7373 actionError: '',
74+ /** The "add an agent" assistant is shown in the centre. */
75+ assistant: false,
76+ /** An agent just added with the assistant: its conversation opens once it is loaded. */
77+ pendingAgent: '',
78+ /** The command palette (⌘K) is open. */
79+ paletteOpen: false,
80+ /** Incremented to ask the channels panel for its "new channel" field (⌘N). */
81+ newChannelRequest: 0,
7482 })
7583
7684 // The channels whose conversation was loaded from Go: from then on, the
@@ -92,6 +100,11 @@ async function refreshAgents() {
92100 const direct = channelOf(appState.selectedChannel)?.direct
93101 if (direct && !names.includes(direct)) appState.selectedChannel = ''
94102 if (appState.workspaceAgent && !names.includes(appState.workspaceAgent)) appState.workspaceAgent = ''
103+ if (appState.pendingAgent && names.includes(appState.pendingAgent)) {
104+ const agent = appState.pendingAgent
105+ appState.pendingAgent = ''
106+ void openDirect(agent)
107+ }
95108 }
96109
97110 async function refreshChannels() {
@@ -138,6 +151,7 @@ export async function act(action: () => Promise<unknown>) {
138151 export async function selectChannel(id: string) {
139152 conversationOf(id) // shown empty until loaded
140153 appState.workspaceAgent = ''
154+ appState.assistant = false
141155 appState.selectedChannel = id
142156 appState.unread[id] = 0
143157 const channel = channelOf(id)
@@ -152,9 +166,26 @@ export async function selectChannel(id: string) {
152166
153167 /** Shows the workspace of agent (files, terminal, log) in the centre. */
154168 export function openWorkspace(agent: string) {
169+ appState.assistant = false
155170 appState.workspaceAgent = agent
156171 }
157172
173+/** Shows the "add an agent" assistant in the centre. */
174+export function openAssistant() {
175+ appState.workspaceAgent = ''
176+ appState.assistant = true
177+}
178+
179+/**
180+ * Called once the assistant has written the agent to the configuration:
181+ * its direct conversation opens when the reloaded configuration lists it.
182+ */
183+export function agentAdded(name: string) {
184+ appState.assistant = false
185+ appState.pendingAgent = name
186+ if (appState.agents.some((agent) => agent.name === name)) void refreshAgents()
187+}
188+
158189 /** Opens the direct conversation with agent, created the first time. */
159190 export function openDirect(agent: string) {
160191 return act(async () => {
@@ -71,6 +71,14 @@ export const appState = $state({
71 workspaceRevision: {} as Record<string, number>,71 workspaceRevision: {} as Record<string, number>,
72 /** The last error returned by an action, shown to the user. */72 /** The last error returned by an action, shown to the user. */
73 actionError: '',73 actionError: '',
74+ /** The "add an agent" assistant is shown in the centre. */
75+ assistant: false,
76+ /** An agent just added with the assistant: its conversation opens once it is loaded. */
77+ pendingAgent: '',
78+ /** The command palette (⌘K) is open. */
79+ paletteOpen: false,
80+ /** Incremented to ask the channels panel for its "new channel" field (⌘N). */
81+ newChannelRequest: 0,
74 })82 })
75 83
76 // The channels whose conversation was loaded from Go: from then on, the84 // The channels whose conversation was loaded from Go: from then on, the
@@ -92,6 +100,11 @@ async function refreshAgents() {
92 const direct = channelOf(appState.selectedChannel)?.direct100 const direct = channelOf(appState.selectedChannel)?.direct
93 if (direct && !names.includes(direct)) appState.selectedChannel = ''101 if (direct && !names.includes(direct)) appState.selectedChannel = ''
94 if (appState.workspaceAgent && !names.includes(appState.workspaceAgent)) appState.workspaceAgent = ''102 if (appState.workspaceAgent && !names.includes(appState.workspaceAgent)) appState.workspaceAgent = ''
103+ if (appState.pendingAgent && names.includes(appState.pendingAgent)) {
104+ const agent = appState.pendingAgent
105+ appState.pendingAgent = ''
106+ void openDirect(agent)
107+ }
95 }108 }
96 109
97 async function refreshChannels() {110 async function refreshChannels() {
@@ -138,6 +151,7 @@ export async function act(action: () => Promise<unknown>) {
138 export async function selectChannel(id: string) {151 export async function selectChannel(id: string) {
139 conversationOf(id) // shown empty until loaded152 conversationOf(id) // shown empty until loaded
140 appState.workspaceAgent = ''153 appState.workspaceAgent = ''
154+ appState.assistant = false
141 appState.selectedChannel = id155 appState.selectedChannel = id
142 appState.unread[id] = 0156 appState.unread[id] = 0
143 const channel = channelOf(id)157 const channel = channelOf(id)
@@ -152,9 +166,26 @@ export async function selectChannel(id: string) {
152 166
153 /** Shows the workspace of agent (files, terminal, log) in the centre. */167 /** Shows the workspace of agent (files, terminal, log) in the centre. */
154 export function openWorkspace(agent: string) {168 export function openWorkspace(agent: string) {
169+ appState.assistant = false
155 appState.workspaceAgent = agent170 appState.workspaceAgent = agent
156 }171 }
157 172
173+/** Shows the "add an agent" assistant in the centre. */
174+export function openAssistant() {
175+ appState.workspaceAgent = ''
176+ appState.assistant = true
177+}
178+
179+/**
180+ * Called once the assistant has written the agent to the configuration:
181+ * its direct conversation opens when the reloaded configuration lists it.
182+ */
183+export function agentAdded(name: string) {
184+ appState.assistant = false
185+ appState.pendingAgent = name
186+ if (appState.agents.some((agent) => agent.name === name)) void refreshAgents()
187+}
188+
158 /** Opens the direct conversation with agent, created the first time. */189 /** Opens the direct conversation with agent, created the first time. */
159 export function openDirect(agent: string) {190 export function openDirect(agent: string) {
160 return act(async () => {191 return act(async () => {
modified frontend/src/style.css +25 -4
@@ -10,12 +10,19 @@
1010 --panel-2: #f0f1f3;
1111 --border: #e2e3e6;
1212 --text: #1d1f24;
13- --muted: #6b6f7a;
14- --accent: #3b6fe0;
13+ --muted: #686c77;
14+ --accent: #3667d0;
1515 --danger: #c9354f;
16- --ok: #3e9b4f;
17- --warn: #c98a1b;
16+ --ok: #317b3f;
17+ --warn: #906313;
1818 --code-bg: #f4f5f7;
19+ /* Text colours reach WCAG AA (4.5:1) on --bg, --panel and --panel-2:
20+ lib/contrast.test.ts checks it. */
21+ /* The logo's colours (docs/design/README.md, Logo). */
22+ --logo-ring: #c9cce8;
23+ --logo-centre: #1e2240;
24+ --logo-teal: #12a39a;
25+ --logo-coral: #ff7a59;
1926 --radius: 8px;
2027 --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
2128 --mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
@@ -37,6 +44,9 @@
3744 --ok: #9ece6a;
3845 --warn: #e0af68;
3946 --code-bg: #15161a;
47+ --logo-ring: #4a5078;
48+ --logo-centre: #ffffff;
49+ --logo-teal: #2fd1c5;
4050 color-scheme: dark;
4151 }
4252 }
@@ -53,6 +63,9 @@
5363 --ok: #9ece6a;
5464 --warn: #e0af68;
5565 --code-bg: #15161a;
66+ --logo-ring: #4a5078;
67+ --logo-centre: #ffffff;
68+ --logo-teal: #2fd1c5;
5669 color-scheme: dark;
5770 }
5871
@@ -95,3 +108,11 @@ button:disabled {
95108 opacity: 0.5;
96109 cursor: default;
97110 }
111+
112+/* Issue #35: whoever uses the keyboard always sees where they are. The
113+ composer's text field and the palette's field are inside a bordered box
114+ that already shows it. */
115+:focus-visible {
116+ outline: 2px solid var(--accent);
117+ outline-offset: 1px;
118+}
@@ -10,12 +10,19 @@
10 --panel-2: #f0f1f3;10 --panel-2: #f0f1f3;
11 --border: #e2e3e6;11 --border: #e2e3e6;
12 --text: #1d1f24;12 --text: #1d1f24;
13- --muted: #6b6f7a;13+ --muted: #686c77;
14- --accent: #3b6fe0;14+ --accent: #3667d0;
15 --danger: #c9354f;15 --danger: #c9354f;
16- --ok: #3e9b4f;16+ --ok: #317b3f;
17- --warn: #c98a1b;17+ --warn: #906313;
18 --code-bg: #f4f5f7;18 --code-bg: #f4f5f7;
19+ /* Text colours reach WCAG AA (4.5:1) on --bg, --panel and --panel-2:
20+ lib/contrast.test.ts checks it. */
21+ /* The logo's colours (docs/design/README.md, Logo). */
22+ --logo-ring: #c9cce8;
23+ --logo-centre: #1e2240;
24+ --logo-teal: #12a39a;
25+ --logo-coral: #ff7a59;
19 --radius: 8px;26 --radius: 8px;
20 --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;27 --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
21 --mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;28 --mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
@@ -37,6 +44,9 @@
37 --ok: #9ece6a;44 --ok: #9ece6a;
38 --warn: #e0af68;45 --warn: #e0af68;
39 --code-bg: #15161a;46 --code-bg: #15161a;
47+ --logo-ring: #4a5078;
48+ --logo-centre: #ffffff;
49+ --logo-teal: #2fd1c5;
40 color-scheme: dark;50 color-scheme: dark;
41 }51 }
42 }52 }
@@ -53,6 +63,9 @@
53 --ok: #9ece6a;63 --ok: #9ece6a;
54 --warn: #e0af68;64 --warn: #e0af68;
55 --code-bg: #15161a;65 --code-bg: #15161a;
66+ --logo-ring: #4a5078;
67+ --logo-centre: #ffffff;
68+ --logo-teal: #2fd1c5;
56 color-scheme: dark;69 color-scheme: dark;
57 }70 }
58 71
@@ -95,3 +108,11 @@ button:disabled {
95 opacity: 0.5;108 opacity: 0.5;
96 cursor: default;109 cursor: default;
97 }110 }
111+
112+/* Issue #35: whoever uses the keyboard always sees where they are. The
113+ composer's text field and the palette's field are inside a bordered box
114+ that already shows it. */
115+:focus-visible {
116+ outline: 2px solid var(--accent);
117+ outline-offset: 1px;
118+}
modified frontend/vite.config.ts +6 -0
@@ -1,3 +1,4 @@
1+/// <reference types="vitest/config" />
12 import { defineConfig } from "vite";
23 import { svelte } from "@sveltejs/vite-plugin-svelte";
34 import wails from "@wailsio/runtime/plugins/vite";
@@ -10,4 +11,9 @@ export default defineConfig({
1011 strictPort: true,
1112 },
1213 plugins: [svelte(), wails("./bindings")],
14+ test: {
15+ // lib/contrast.test.ts reads the theme from style.css (?raw): Vitest
16+ // leaves CSS files empty unless they are processed.
17+ css: { include: [/style\.css/] },
18+ },
1319 });
@@ -1,3 +1,4 @@
1+/// <reference types="vitest/config" />
1 import { defineConfig } from "vite";2 import { defineConfig } from "vite";
2 import { svelte } from "@sveltejs/vite-plugin-svelte";3 import { svelte } from "@sveltejs/vite-plugin-svelte";
3 import wails from "@wailsio/runtime/plugins/vite";4 import wails from "@wailsio/runtime/plugins/vite";
@@ -10,4 +11,9 @@ export default defineConfig({
10 strictPort: true,11 strictPort: true,
11 },12 },
12 plugins: [svelte(), wails("./bindings")],13 plugins: [svelte(), wails("./bindings")],
14+ test: {
15+ // lib/contrast.test.ts reads the theme from style.css (?raw): Vitest
16+ // leaves CSS files empty unless they are processed.
17+ css: { include: [/style\.css/] },
18+ },
13 });19 });
modified internal/app/app.go +3 -0
@@ -38,6 +38,9 @@ type Options struct {
3838 // PickFile asks the user for a configuration file (native dialog),
3939 // starting in folder; it returns "" when the user cancels.
4040 PickFile func(folder string) (string, error)
41+ // PickFolder asks the user for a folder (native dialog), starting in
42+ // folder; it returns "" when the user cancels.
43+ PickFolder func(folder string) (string, error)
4144 // OpenStore opens the history of the configuration file at path; nil
4245 // opens it in the user's configuration folder (store.DirFor).
4346 OpenStore func(configPath string) (*store.Store, error)
@@ -38,6 +38,9 @@ type Options struct {
38 // PickFile asks the user for a configuration file (native dialog),38 // PickFile asks the user for a configuration file (native dialog),
39 // starting in folder; it returns "" when the user cancels.39 // starting in folder; it returns "" when the user cancels.
40 PickFile func(folder string) (string, error)40 PickFile func(folder string) (string, error)
41+ // PickFolder asks the user for a folder (native dialog), starting in
42+ // folder; it returns "" when the user cancels.
43+ PickFolder func(folder string) (string, error)
41 // OpenStore opens the history of the configuration file at path; nil44 // OpenStore opens the history of the configuration file at path; nil
42 // opens it in the user's configuration folder (store.DirFor).45 // opens it in the user's configuration folder (store.DirFor).
43 OpenStore func(configPath string) (*store.Store, error)46 OpenStore func(configPath string) (*store.Store, error)
modified internal/app/app_test.go +52 -0
@@ -435,3 +435,55 @@ func TestATerminalRunsInTheAgentsFolder(t *testing.T) {
435435 core.Shutdown(context.Background())
436436 waitFor(t, func() bool { return len(recorder.Named(events.TerminalExit)) == 1 })
437437 }
438+
439+func TestTheAssistantAddsAnAgentAndCreatesItsFolder(t *testing.T) {
440+ directory := t.TempDir()
441+ path := filepath.Join(directory, "config.yaml")
442+ write(t, path, "# my team\n"+twoAgents)
443+ service := &app.ConfigService{Core: app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter})}
444+
445+ problems, err := service.AddAgent(app.AgentForm{Name: "carol", Title: "Carol", Program: "sh", Arguments: "-c 'echo hi'", Workspace: "agents/carol"})
446+ if err != nil || len(problems) != 0 {
447+ t.Fatalf("add: %v %v", problems, err)
448+ }
449+ if info, err := os.Stat(filepath.Join(directory, "agents", "carol")); err != nil || !info.IsDir() {
450+ t.Fatalf("the agent's folder is created next to the configuration: %v", err)
451+ }
452+ content, _ := os.ReadFile(path)
453+ cfg, err := config.Parse(content, path)
454+ if err != nil || !strings.HasPrefix(string(content), "# my team") {
455+ t.Fatalf("the file stays valid and keeps its comments: %v\n%s", err, content)
456+ }
457+ if carol, ok := cfg.Agent("carol"); !ok || carol.Args[1] != "echo hi" {
458+ t.Fatalf("carol not added:\n%s", content)
459+ }
460+
461+ problems, err = service.AddAgent(app.AgentForm{Name: "alice", Program: "sh", Workspace: "agents/alice2"})
462+ if err != nil || len(problems) == 0 {
463+ t.Fatalf("a name already used is a problem, not an error: %v %v", problems, err)
464+ }
465+ if after, _ := os.ReadFile(path); string(after) != string(content) {
466+ t.Fatal("nothing is written when the result is invalid")
467+ }
468+ if _, err := service.AddAgent(app.AgentForm{Name: "dave"}); err == nil {
469+ t.Fatal("an incomplete form is an error")
470+ }
471+}
472+
473+func TestChooseFolderStartsInTheHomeFolder(t *testing.T) {
474+ path := filepath.Join(t.TempDir(), "config.yaml")
475+ write(t, path, twoAgents)
476+ var from string
477+ core := app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter, PickFolder: func(folder string) (string, error) {
478+ from = folder
479+ return "/picked", nil
480+ }})
481+ got, err := (&app.ConfigService{Core: core}).ChooseFolder()
482+ home, _ := os.UserHomeDir()
483+ if err != nil || got != "/picked" || from != home {
484+ t.Fatalf("got %q %v, started in %q", got, err, from)
485+ }
486+ if _, err := (&app.ConfigService{Core: app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter})}).ChooseFolder(); err == nil {
487+ t.Fatal("without a dialog, ChooseFolder is an error")
488+ }
489+}
@@ -435,3 +435,55 @@ func TestATerminalRunsInTheAgentsFolder(t *testing.T) {
435 core.Shutdown(context.Background())435 core.Shutdown(context.Background())
436 waitFor(t, func() bool { return len(recorder.Named(events.TerminalExit)) == 1 })436 waitFor(t, func() bool { return len(recorder.Named(events.TerminalExit)) == 1 })
437 }437 }
438+
439+func TestTheAssistantAddsAnAgentAndCreatesItsFolder(t *testing.T) {
440+ directory := t.TempDir()
441+ path := filepath.Join(directory, "config.yaml")
442+ write(t, path, "# my team\n"+twoAgents)
443+ service := &app.ConfigService{Core: app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter})}
444+
445+ problems, err := service.AddAgent(app.AgentForm{Name: "carol", Title: "Carol", Program: "sh", Arguments: "-c 'echo hi'", Workspace: "agents/carol"})
446+ if err != nil || len(problems) != 0 {
447+ t.Fatalf("add: %v %v", problems, err)
448+ }
449+ if info, err := os.Stat(filepath.Join(directory, "agents", "carol")); err != nil || !info.IsDir() {
450+ t.Fatalf("the agent's folder is created next to the configuration: %v", err)
451+ }
452+ content, _ := os.ReadFile(path)
453+ cfg, err := config.Parse(content, path)
454+ if err != nil || !strings.HasPrefix(string(content), "# my team") {
455+ t.Fatalf("the file stays valid and keeps its comments: %v\n%s", err, content)
456+ }
457+ if carol, ok := cfg.Agent("carol"); !ok || carol.Args[1] != "echo hi" {
458+ t.Fatalf("carol not added:\n%s", content)
459+ }
460+
461+ problems, err = service.AddAgent(app.AgentForm{Name: "alice", Program: "sh", Workspace: "agents/alice2"})
462+ if err != nil || len(problems) == 0 {
463+ t.Fatalf("a name already used is a problem, not an error: %v %v", problems, err)
464+ }
465+ if after, _ := os.ReadFile(path); string(after) != string(content) {
466+ t.Fatal("nothing is written when the result is invalid")
467+ }
468+ if _, err := service.AddAgent(app.AgentForm{Name: "dave"}); err == nil {
469+ t.Fatal("an incomplete form is an error")
470+ }
471+}
472+
473+func TestChooseFolderStartsInTheHomeFolder(t *testing.T) {
474+ path := filepath.Join(t.TempDir(), "config.yaml")
475+ write(t, path, twoAgents)
476+ var from string
477+ core := app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter, PickFolder: func(folder string) (string, error) {
478+ from = folder
479+ return "/picked", nil
480+ }})
481+ got, err := (&app.ConfigService{Core: core}).ChooseFolder()
482+ home, _ := os.UserHomeDir()
483+ if err != nil || got != "/picked" || from != home {
484+ t.Fatalf("got %q %v, started in %q", got, err, from)
485+ }
486+ if _, err := (&app.ConfigService{Core: app.New(config.Location{Path: path, Source: config.SourceFlag}, app.Options{Starter: starter})}).ChooseFolder(); err == nil {
487+ t.Fatal("without a dialog, ChooseFolder is an error")
488+ }
489+}
added internal/app/newagent.go +178 -0
new file mode 100644
@@ -0,0 +1,178 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package app
4+
5+import (
6+ "errors"
7+ "fmt"
8+ "os"
9+ "path/filepath"
10+ "strings"
11+
12+ "git.rickub.com/bots-garden/sidekicks/internal/config"
13+)
14+
15+// Where an agent added with the assistant runs.
16+const (
17+ EnvironmentLocal = "local"
18+ EnvironmentContainer = "container"
19+ EnvironmentSandbox = "sbx"
20+)
21+
22+// AgentForm is what the "add an agent" assistant asks for. Sidekicks turns
23+// it into the agent's entry of the configuration file (see entryOf), so the
24+// user never writes YAML.
25+type AgentForm struct {
26+ Name string `json:"name"`
27+ Title string `json:"title"`
28+ Icon string `json:"icon"`
29+ Description string `json:"description"`
30+ // Environment is local, container or sbx.
31+ Environment string `json:"environment"`
32+ // Box is the name of the Docker container or of the sbx sandbox.
33+ Box string `json:"box"`
34+ // Program is the agent's program, such as mm.
35+ Program string `json:"program"`
36+ // Arguments are typed on one line, as in a terminal: quotes group words.
37+ Arguments string `json:"arguments"`
38+ // Workspace is the agent's folder on this computer. It is created if needed.
39+ Workspace string `json:"workspace"`
40+}
41+
42+// errMissingField is returned when the interface sends an incomplete form;
43+// the interface checks the required fields itself before sending it.
44+var errMissingField = errors.New("missing field")
45+
46+// entryOf turns a form into a configuration entry. An agent in a container
47+// or a sandbox is started with "docker exec -i" or "sbx exec -i": ACP goes
48+// through stdin and stdout, so -i is needed and -t must not be used.
49+//
50+// entry, err := entryOf(AgentForm{Name: "scout", Environment: "container", Box: "scout-box", Program: "mm", Workspace: "/agents/scout"})
51+// // entry.Command == "docker", entry.Args == [exec -i scout-box mm]
52+func entryOf(form AgentForm) (config.AgentEntry, error) {
53+ form = trimmed(form)
54+ if form.Name == "" || form.Program == "" || form.Workspace == "" {
55+ return config.AgentEntry{}, fmt.Errorf("%w: name, program and folder are required", errMissingField)
56+ }
57+ arguments, err := splitArguments(form.Arguments)
58+ if err != nil {
59+ return config.AgentEntry{}, err
60+ }
61+ entry := config.AgentEntry{
62+ Name: form.Name, Title: form.Title, Icon: form.Icon, Description: form.Description,
63+ Command: form.Program, Args: arguments, Workspace: form.Workspace,
64+ }
65+ switch form.Environment {
66+ case "", EnvironmentLocal:
67+ return entry, nil
68+ case EnvironmentContainer, EnvironmentSandbox:
69+ if form.Box == "" {
70+ return config.AgentEntry{}, fmt.Errorf("%w: the name of the container or sandbox is required", errMissingField)
71+ }
72+ tool, runtime := "docker", &config.RuntimeEntry{Type: form.Environment, Container: form.Box}
73+ if form.Environment == EnvironmentSandbox {
74+ tool, runtime = "sbx", &config.RuntimeEntry{Type: form.Environment, Sandbox: form.Box}
75+ }
76+ entry.Command = tool
77+ entry.Args = append([]string{"exec", "-i", form.Box, form.Program}, arguments...)
78+ entry.Runtime = runtime
79+ return entry, nil
80+ default:
81+ return config.AgentEntry{}, fmt.Errorf("unknown environment %q", form.Environment)
82+ }
83+}
84+
85+func trimmed(form AgentForm) AgentForm {
86+ for _, field := range []*string{&form.Name, &form.Title, &form.Icon, &form.Description, &form.Environment, &form.Box, &form.Program, &form.Workspace} {
87+ *field = strings.TrimSpace(*field)
88+ }
89+ return form
90+}
91+
92+// splitArguments splits a line of arguments like a shell does for simple
93+// cases: spaces separate, single or double quotes group, nothing else is
94+// interpreted.
95+//
96+// splitArguments(`-acp -config "my agent.yaml"`) // [-acp -config my agent.yaml]
97+func splitArguments(line string) ([]string, error) {
98+ var arguments []string
99+ var current strings.Builder
100+ inWord, quote := false, rune(0)
101+ for _, character := range line {
102+ switch {
103+ case quote != 0 && character == quote:
104+ quote = 0
105+ case quote != 0:
106+ current.WriteRune(character)
107+ case character == '"' || character == '\'':
108+ quote, inWord = character, true
109+ case character == ' ' || character == '\t':
110+ if inWord {
111+ arguments = append(arguments, current.String())
112+ current.Reset()
113+ inWord = false
114+ }
115+ default:
116+ current.WriteRune(character)
117+ inWord = true
118+ }
119+ }
120+ if quote != 0 {
121+ return nil, fmt.Errorf("a quote is not closed in %q", line)
122+ }
123+ if inWord {
124+ arguments = append(arguments, current.String())
125+ }
126+ return arguments, nil
127+}
128+
129+// AddAgent adds the agent described by form to the configuration file,
130+// keeping its comments, and creates the agent's folder when it does not
131+// exist. When the result would not be valid (a name already used, a name
132+// with spaces…), nothing is written and the problems are returned. The file
133+// watcher then reloads the configuration, as after a save in Settings.
134+//
135+// problems, err := (&ConfigService{Core: core}).AddAgent(AgentForm{Name: "alice", Program: "mm", Workspace: "~/agents/alice"})
136+func (s *ConfigService) AddAgent(form AgentForm) ([]string, error) {
137+ entry, err := entryOf(form)
138+ if err != nil {
139+ return nil, err
140+ }
141+ path := s.Core.path()
142+ content, err := os.ReadFile(path)
143+ if err != nil {
144+ return nil, err
145+ }
146+ updated, err := config.AppendAgent(content, entry)
147+ var configError *config.Error
148+ if errors.As(err, &configError) {
149+ return messages(configError.Problems), nil
150+ }
151+ if err != nil {
152+ return nil, err
153+ }
154+ folder := entry.Workspace
155+ if folder == "~" || strings.HasPrefix(folder, "~/") {
156+ if home, err := os.UserHomeDir(); err == nil {
157+ folder = filepath.Join(home, strings.TrimPrefix(folder, "~"))
158+ }
159+ }
160+ if !filepath.IsAbs(folder) {
161+ folder = filepath.Join(filepath.Dir(path), folder)
162+ }
163+ if err := os.MkdirAll(folder, 0o755); err != nil {
164+ return nil, err
165+ }
166+ return []string{}, config.Save(path, updated)
167+}
168+
169+// ChooseFolder asks the user for a folder, starting in their home folder,
170+// for the assistant's "agent's folder" field. It returns "" when the user
171+// cancelled.
172+func (s *ConfigService) ChooseFolder() (string, error) {
173+ if s.Core.options.PickFolder == nil {
174+ return "", errors.New("no folder dialog available")
175+ }
176+ home, _ := os.UserHomeDir()
177+ return s.Core.options.PickFolder(home)
178+}
new file mode 100644
@@ -0,0 +1,178 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package app
4+
5+import (
6+ "errors"
7+ "fmt"
8+ "os"
9+ "path/filepath"
10+ "strings"
11+
12+ "git.rickub.com/bots-garden/sidekicks/internal/config"
13+)
14+
15+// Where an agent added with the assistant runs.
16+const (
17+ EnvironmentLocal = "local"
18+ EnvironmentContainer = "container"
19+ EnvironmentSandbox = "sbx"
20+)
21+
22+// AgentForm is what the "add an agent" assistant asks for. Sidekicks turns
23+// it into the agent's entry of the configuration file (see entryOf), so the
24+// user never writes YAML.
25+type AgentForm struct {
26+ Name string `json:"name"`
27+ Title string `json:"title"`
28+ Icon string `json:"icon"`
29+ Description string `json:"description"`
30+ // Environment is local, container or sbx.
31+ Environment string `json:"environment"`
32+ // Box is the name of the Docker container or of the sbx sandbox.
33+ Box string `json:"box"`
34+ // Program is the agent's program, such as mm.
35+ Program string `json:"program"`
36+ // Arguments are typed on one line, as in a terminal: quotes group words.
37+ Arguments string `json:"arguments"`
38+ // Workspace is the agent's folder on this computer. It is created if needed.
39+ Workspace string `json:"workspace"`
40+}
41+
42+// errMissingField is returned when the interface sends an incomplete form;
43+// the interface checks the required fields itself before sending it.
44+var errMissingField = errors.New("missing field")
45+
46+// entryOf turns a form into a configuration entry. An agent in a container
47+// or a sandbox is started with "docker exec -i" or "sbx exec -i": ACP goes
48+// through stdin and stdout, so -i is needed and -t must not be used.
49+//
50+// entry, err := entryOf(AgentForm{Name: "scout", Environment: "container", Box: "scout-box", Program: "mm", Workspace: "/agents/scout"})
51+// // entry.Command == "docker", entry.Args == [exec -i scout-box mm]
52+func entryOf(form AgentForm) (config.AgentEntry, error) {
53+ form = trimmed(form)
54+ if form.Name == "" || form.Program == "" || form.Workspace == "" {
55+ return config.AgentEntry{}, fmt.Errorf("%w: name, program and folder are required", errMissingField)
56+ }
57+ arguments, err := splitArguments(form.Arguments)
58+ if err != nil {
59+ return config.AgentEntry{}, err
60+ }
61+ entry := config.AgentEntry{
62+ Name: form.Name, Title: form.Title, Icon: form.Icon, Description: form.Description,
63+ Command: form.Program, Args: arguments, Workspace: form.Workspace,
64+ }
65+ switch form.Environment {
66+ case "", EnvironmentLocal:
67+ return entry, nil
68+ case EnvironmentContainer, EnvironmentSandbox:
69+ if form.Box == "" {
70+ return config.AgentEntry{}, fmt.Errorf("%w: the name of the container or sandbox is required", errMissingField)
71+ }
72+ tool, runtime := "docker", &config.RuntimeEntry{Type: form.Environment, Container: form.Box}
73+ if form.Environment == EnvironmentSandbox {
74+ tool, runtime = "sbx", &config.RuntimeEntry{Type: form.Environment, Sandbox: form.Box}
75+ }
76+ entry.Command = tool
77+ entry.Args = append([]string{"exec", "-i", form.Box, form.Program}, arguments...)
78+ entry.Runtime = runtime
79+ return entry, nil
80+ default:
81+ return config.AgentEntry{}, fmt.Errorf("unknown environment %q", form.Environment)
82+ }
83+}
84+
85+func trimmed(form AgentForm) AgentForm {
86+ for _, field := range []*string{&form.Name, &form.Title, &form.Icon, &form.Description, &form.Environment, &form.Box, &form.Program, &form.Workspace} {
87+ *field = strings.TrimSpace(*field)
88+ }
89+ return form
90+}
91+
92+// splitArguments splits a line of arguments like a shell does for simple
93+// cases: spaces separate, single or double quotes group, nothing else is
94+// interpreted.
95+//
96+// splitArguments(`-acp -config "my agent.yaml"`) // [-acp -config my agent.yaml]
97+func splitArguments(line string) ([]string, error) {
98+ var arguments []string
99+ var current strings.Builder
100+ inWord, quote := false, rune(0)
101+ for _, character := range line {
102+ switch {
103+ case quote != 0 && character == quote:
104+ quote = 0
105+ case quote != 0:
106+ current.WriteRune(character)
107+ case character == '"' || character == '\'':
108+ quote, inWord = character, true
109+ case character == ' ' || character == '\t':
110+ if inWord {
111+ arguments = append(arguments, current.String())
112+ current.Reset()
113+ inWord = false
114+ }
115+ default:
116+ current.WriteRune(character)
117+ inWord = true
118+ }
119+ }
120+ if quote != 0 {
121+ return nil, fmt.Errorf("a quote is not closed in %q", line)
122+ }
123+ if inWord {
124+ arguments = append(arguments, current.String())
125+ }
126+ return arguments, nil
127+}
128+
129+// AddAgent adds the agent described by form to the configuration file,
130+// keeping its comments, and creates the agent's folder when it does not
131+// exist. When the result would not be valid (a name already used, a name
132+// with spaces…), nothing is written and the problems are returned. The file
133+// watcher then reloads the configuration, as after a save in Settings.
134+//
135+// problems, err := (&ConfigService{Core: core}).AddAgent(AgentForm{Name: "alice", Program: "mm", Workspace: "~/agents/alice"})
136+func (s *ConfigService) AddAgent(form AgentForm) ([]string, error) {
137+ entry, err := entryOf(form)
138+ if err != nil {
139+ return nil, err
140+ }
141+ path := s.Core.path()
142+ content, err := os.ReadFile(path)
143+ if err != nil {
144+ return nil, err
145+ }
146+ updated, err := config.AppendAgent(content, entry)
147+ var configError *config.Error
148+ if errors.As(err, &configError) {
149+ return messages(configError.Problems), nil
150+ }
151+ if err != nil {
152+ return nil, err
153+ }
154+ folder := entry.Workspace
155+ if folder == "~" || strings.HasPrefix(folder, "~/") {
156+ if home, err := os.UserHomeDir(); err == nil {
157+ folder = filepath.Join(home, strings.TrimPrefix(folder, "~"))
158+ }
159+ }
160+ if !filepath.IsAbs(folder) {
161+ folder = filepath.Join(filepath.Dir(path), folder)
162+ }
163+ if err := os.MkdirAll(folder, 0o755); err != nil {
164+ return nil, err
165+ }
166+ return []string{}, config.Save(path, updated)
167+}
168+
169+// ChooseFolder asks the user for a folder, starting in their home folder,
170+// for the assistant's "agent's folder" field. It returns "" when the user
171+// cancelled.
172+func (s *ConfigService) ChooseFolder() (string, error) {
173+ if s.Core.options.PickFolder == nil {
174+ return "", errors.New("no folder dialog available")
175+ }
176+ home, _ := os.UserHomeDir()
177+ return s.Core.options.PickFolder(home)
178+}
added internal/app/newagent_internal_test.go +76 -0
new file mode 100644
@@ -0,0 +1,76 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package app
4+
5+import (
6+ "errors"
7+ "reflect"
8+ "testing"
9+)
10+
11+func TestSplitArgumentsReadsALineLikeAShell(t *testing.T) {
12+ cases := map[string][]string{
13+ "": nil,
14+ " ": nil,
15+ "-acp": {"-acp"},
16+ "-acp -config agent.yaml": {"-acp", "-config", "agent.yaml"},
17+ `-config "my agent.yaml" -v`: {"-config", "my agent.yaml", "-v"},
18+ `--name 'it''s' ""`: {"--name", "its", ""},
19+ "\t-a\tb ": {"-a", "b"},
20+ }
21+ for line, want := range cases {
22+ got, err := splitArguments(line)
23+ if err != nil || !reflect.DeepEqual(got, want) {
24+ t.Errorf("splitArguments(%q) = %q, %v; want %q", line, got, err, want)
25+ }
26+ }
27+ if _, err := splitArguments(`-config "unfinished`); err == nil {
28+ t.Error("an unclosed quote is an error")
29+ }
30+}
31+
32+func TestEntryOfALocalAgent(t *testing.T) {
33+ entry, err := entryOf(AgentForm{Name: " alice ", Title: "Alice", Program: "mm", Arguments: "-acp -config agent.yaml", Workspace: "~/agents/alice"})
34+ if err != nil {
35+ t.Fatal(err)
36+ }
37+ if entry.Name != "alice" || entry.Command != "mm" || !reflect.DeepEqual(entry.Args, []string{"-acp", "-config", "agent.yaml"}) || entry.Runtime != nil {
38+ t.Fatalf("got %+v", entry)
39+ }
40+}
41+
42+func TestEntryOfAnAgentInAContainerOrASandbox(t *testing.T) {
43+ container, err := entryOf(AgentForm{Name: "scout", Environment: EnvironmentContainer, Box: "scout-box", Program: "mm", Arguments: "-acp", Workspace: "/agents/scout"})
44+ if err != nil {
45+ t.Fatal(err)
46+ }
47+ if container.Command != "docker" || !reflect.DeepEqual(container.Args, []string{"exec", "-i", "scout-box", "mm", "-acp"}) ||
48+ container.Runtime == nil || container.Runtime.Type != "container" || container.Runtime.Container != "scout-box" {
49+ t.Fatalf("container: %+v %+v", container, container.Runtime)
50+ }
51+ sandbox, err := entryOf(AgentForm{Name: "milo", Environment: EnvironmentSandbox, Box: "milo-sbx", Program: "mm", Workspace: "/agents/milo"})
52+ if err != nil {
53+ t.Fatal(err)
54+ }
55+ if sandbox.Command != "sbx" || !reflect.DeepEqual(sandbox.Args, []string{"exec", "-i", "milo-sbx", "mm"}) ||
56+ sandbox.Runtime == nil || sandbox.Runtime.Type != "sbx" || sandbox.Runtime.Sandbox != "milo-sbx" {
57+ t.Fatalf("sandbox: %+v %+v", sandbox, sandbox.Runtime)
58+ }
59+}
60+
61+func TestEntryOfRefusesAnIncompleteForm(t *testing.T) {
62+ incomplete := []AgentForm{
63+ {Program: "mm", Workspace: "/a"},
64+ {Name: "a", Workspace: "/a"},
65+ {Name: "a", Program: "mm"},
66+ {Name: "a", Program: "mm", Workspace: "/a", Environment: EnvironmentContainer},
67+ }
68+ for _, form := range incomplete {
69+ if _, err := entryOf(form); !errors.Is(err, errMissingField) {
70+ t.Errorf("%+v: got %v", form, err)
71+ }
72+ }
73+ if _, err := entryOf(AgentForm{Name: "a", Program: "mm", Workspace: "/a", Environment: "cloud"}); err == nil {
74+ t.Error("an unknown environment is refused")
75+ }
76+}
new file mode 100644
@@ -0,0 +1,76 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package app
4+
5+import (
6+ "errors"
7+ "reflect"
8+ "testing"
9+)
10+
11+func TestSplitArgumentsReadsALineLikeAShell(t *testing.T) {
12+ cases := map[string][]string{
13+ "": nil,
14+ " ": nil,
15+ "-acp": {"-acp"},
16+ "-acp -config agent.yaml": {"-acp", "-config", "agent.yaml"},
17+ `-config "my agent.yaml" -v`: {"-config", "my agent.yaml", "-v"},
18+ `--name 'it''s' ""`: {"--name", "its", ""},
19+ "\t-a\tb ": {"-a", "b"},
20+ }
21+ for line, want := range cases {
22+ got, err := splitArguments(line)
23+ if err != nil || !reflect.DeepEqual(got, want) {
24+ t.Errorf("splitArguments(%q) = %q, %v; want %q", line, got, err, want)
25+ }
26+ }
27+ if _, err := splitArguments(`-config "unfinished`); err == nil {
28+ t.Error("an unclosed quote is an error")
29+ }
30+}
31+
32+func TestEntryOfALocalAgent(t *testing.T) {
33+ entry, err := entryOf(AgentForm{Name: " alice ", Title: "Alice", Program: "mm", Arguments: "-acp -config agent.yaml", Workspace: "~/agents/alice"})
34+ if err != nil {
35+ t.Fatal(err)
36+ }
37+ if entry.Name != "alice" || entry.Command != "mm" || !reflect.DeepEqual(entry.Args, []string{"-acp", "-config", "agent.yaml"}) || entry.Runtime != nil {
38+ t.Fatalf("got %+v", entry)
39+ }
40+}
41+
42+func TestEntryOfAnAgentInAContainerOrASandbox(t *testing.T) {
43+ container, err := entryOf(AgentForm{Name: "scout", Environment: EnvironmentContainer, Box: "scout-box", Program: "mm", Arguments: "-acp", Workspace: "/agents/scout"})
44+ if err != nil {
45+ t.Fatal(err)
46+ }
47+ if container.Command != "docker" || !reflect.DeepEqual(container.Args, []string{"exec", "-i", "scout-box", "mm", "-acp"}) ||
48+ container.Runtime == nil || container.Runtime.Type != "container" || container.Runtime.Container != "scout-box" {
49+ t.Fatalf("container: %+v %+v", container, container.Runtime)
50+ }
51+ sandbox, err := entryOf(AgentForm{Name: "milo", Environment: EnvironmentSandbox, Box: "milo-sbx", Program: "mm", Workspace: "/agents/milo"})
52+ if err != nil {
53+ t.Fatal(err)
54+ }
55+ if sandbox.Command != "sbx" || !reflect.DeepEqual(sandbox.Args, []string{"exec", "-i", "milo-sbx", "mm"}) ||
56+ sandbox.Runtime == nil || sandbox.Runtime.Type != "sbx" || sandbox.Runtime.Sandbox != "milo-sbx" {
57+ t.Fatalf("sandbox: %+v %+v", sandbox, sandbox.Runtime)
58+ }
59+}
60+
61+func TestEntryOfRefusesAnIncompleteForm(t *testing.T) {
62+ incomplete := []AgentForm{
63+ {Program: "mm", Workspace: "/a"},
64+ {Name: "a", Workspace: "/a"},
65+ {Name: "a", Program: "mm"},
66+ {Name: "a", Program: "mm", Workspace: "/a", Environment: EnvironmentContainer},
67+ }
68+ for _, form := range incomplete {
69+ if _, err := entryOf(form); !errors.Is(err, errMissingField) {
70+ t.Errorf("%+v: got %v", form, err)
71+ }
72+ }
73+ if _, err := entryOf(AgentForm{Name: "a", Program: "mm", Workspace: "/a", Environment: "cloud"}); err == nil {
74+ t.Error("an unknown environment is refused")
75+ }
76+}
modified internal/config/config_test.go +42 -0
@@ -298,6 +298,48 @@ func TestAppendAgentKeepsComments(t *testing.T) {
298298 }
299299 }
300300
301+func TestAppendAgentWritesTheRuntime(t *testing.T) {
302+ entry := config.AgentEntry{
303+ Name: "scout", Command: "docker", Args: []string{"exec", "-i", "scout-box", "mm"}, Workspace: "/agents/scout",
304+ Runtime: &config.RuntimeEntry{Type: "container", Container: "scout-box"},
305+ }
306+ updated, err := config.AppendAgent([]byte(minimal), entry)
307+ if err != nil {
308+ t.Fatal(err)
309+ }
310+ cfg, err := config.Parse(updated, "/tmp/config.yaml")
311+ if err != nil {
312+ t.Fatal(err)
313+ }
314+ scout, _ := cfg.Agent("scout")
315+ if scout.Runtime.Type != "container" || scout.Runtime.Container != "scout-box" || scout.Workspace != "/agents/scout" {
316+ t.Fatalf("runtime not written:\n%s", updated)
317+ }
318+ local, _ := config.AppendAgent([]byte(minimal), config.AgentEntry{Name: "bob", Command: "mm"})
319+ if strings.Contains(string(local), "runtime") {
320+ t.Fatalf("a local agent has no runtime key:\n%s", local)
321+ }
322+}
323+
324+func TestAppendAgentKeepsEmojiReadable(t *testing.T) {
325+ original := "version: 1\nagents:\n - name: alice\n icon: \"👩\"\n description: \"a \\\\U0001F916 literal\"\n command: mm\n"
326+ updated, err := config.AppendAgent([]byte(original), config.AgentEntry{Name: "bob", Icon: "🤖", Command: "mm"})
327+ if err != nil {
328+ t.Fatal(err)
329+ }
330+ text := string(updated)
331+ if !strings.Contains(text, `"👩"`) || !strings.Contains(text, `"🤖"`) || strings.Contains(text, `"\U0001F`) {
332+ t.Fatalf("emoji must stay readable:\n%s", text)
333+ }
334+ cfg, err := config.Parse(updated, "/tmp/config.yaml")
335+ if err != nil {
336+ t.Fatal(err)
337+ }
338+ if alice, _ := cfg.Agent("alice"); alice.Description != `a \U0001F916 literal` {
339+ t.Fatalf("a literal backslash-U text is kept: %q", alice.Description)
340+ }
341+}
342+
301343 func TestAppendAgentRejectsADuplicate(t *testing.T) {
302344 if _, err := config.AppendAgent([]byte(minimal), config.AgentEntry{Name: "alice", Command: "mm"}); err == nil {
303345 t.Fatal("a second alice must be refused")
@@ -298,6 +298,48 @@ func TestAppendAgentKeepsComments(t *testing.T) {
298 }298 }
299 }299 }
300 300
301+func TestAppendAgentWritesTheRuntime(t *testing.T) {
302+ entry := config.AgentEntry{
303+ Name: "scout", Command: "docker", Args: []string{"exec", "-i", "scout-box", "mm"}, Workspace: "/agents/scout",
304+ Runtime: &config.RuntimeEntry{Type: "container", Container: "scout-box"},
305+ }
306+ updated, err := config.AppendAgent([]byte(minimal), entry)
307+ if err != nil {
308+ t.Fatal(err)
309+ }
310+ cfg, err := config.Parse(updated, "/tmp/config.yaml")
311+ if err != nil {
312+ t.Fatal(err)
313+ }
314+ scout, _ := cfg.Agent("scout")
315+ if scout.Runtime.Type != "container" || scout.Runtime.Container != "scout-box" || scout.Workspace != "/agents/scout" {
316+ t.Fatalf("runtime not written:\n%s", updated)
317+ }
318+ local, _ := config.AppendAgent([]byte(minimal), config.AgentEntry{Name: "bob", Command: "mm"})
319+ if strings.Contains(string(local), "runtime") {
320+ t.Fatalf("a local agent has no runtime key:\n%s", local)
321+ }
322+}
323+
324+func TestAppendAgentKeepsEmojiReadable(t *testing.T) {
325+ original := "version: 1\nagents:\n - name: alice\n icon: \"👩\"\n description: \"a \\\\U0001F916 literal\"\n command: mm\n"
326+ updated, err := config.AppendAgent([]byte(original), config.AgentEntry{Name: "bob", Icon: "🤖", Command: "mm"})
327+ if err != nil {
328+ t.Fatal(err)
329+ }
330+ text := string(updated)
331+ if !strings.Contains(text, `"👩"`) || !strings.Contains(text, `"🤖"`) || strings.Contains(text, `"\U0001F`) {
332+ t.Fatalf("emoji must stay readable:\n%s", text)
333+ }
334+ cfg, err := config.Parse(updated, "/tmp/config.yaml")
335+ if err != nil {
336+ t.Fatal(err)
337+ }
338+ if alice, _ := cfg.Agent("alice"); alice.Description != `a \U0001F916 literal` {
339+ t.Fatalf("a literal backslash-U text is kept: %q", alice.Description)
340+ }
341+}
342+
301 func TestAppendAgentRejectsADuplicate(t *testing.T) {343 func TestAppendAgentRejectsADuplicate(t *testing.T) {
302 if _, err := config.AppendAgent([]byte(minimal), config.AgentEntry{Name: "alice", Command: "mm"}); err == nil {344 if _, err := config.AppendAgent([]byte(minimal), config.AgentEntry{Name: "alice", Command: "mm"}); err == nil {
303 t.Fatal("a second alice must be refused")345 t.Fatal("a second alice must be refused")
modified internal/config/default.go +3 -1
@@ -31,8 +31,10 @@ limits:
3131 stopIdleAgentsAfter: 10m
3232
3333 # The agents: call them in a conversation by clicking their name.
34+# The simplest way to add one is the "Add an agent" button of Sidekicks.
3435 agents: []
35-# Example, to copy between the brackets above (removing the "#"):
36+# Or write it yourself. An example (remove the "#", and the "[]" above
37+# while the list is empty):
3638 # - name: alice # the name used with @alice
3739 # title: Alice (assistant) # the name shown
3840 # icon: "👩"
@@ -31,8 +31,10 @@ limits:
31 stopIdleAgentsAfter: 10m31 stopIdleAgentsAfter: 10m
32 32
33 # The agents: call them in a conversation by clicking their name.33 # The agents: call them in a conversation by clicking their name.
34+# The simplest way to add one is the "Add an agent" button of Sidekicks.
34 agents: []35 agents: []
35-# Example, to copy between the brackets above (removing the "#"):36+# Or write it yourself. An example (remove the "#", and the "[]" above
37+# while the list is empty):
36 # - name: alice # the name used with @alice38 # - name: alice # the name used with @alice
37 # title: Alice (assistant) # the name shown39 # title: Alice (assistant) # the name shown
38 # icon: "👩"40 # icon: "👩"
modified internal/config/save.go +44 -2
@@ -7,6 +7,8 @@ import (
77 "errors"
88 "fmt"
99 "path/filepath"
10+ "strconv"
11+ "unicode/utf8"
1012
1113 "go.yaml.in/yaml/v3"
1214 )
@@ -38,6 +40,17 @@ type AgentEntry struct {
3840 Args []string `yaml:"args,omitempty,flow"`
3941 Env map[string]string `yaml:"env,omitempty"`
4042 Workspace string `yaml:"workspace,omitempty"`
43+ // Runtime is left out for an agent that runs on this computer.
44+ Runtime *RuntimeEntry `yaml:"runtime,omitempty"`
45+}
46+
47+// RuntimeEntry is the runtime of an AgentEntry: where its files and its
48+// terminal are when the agent runs in a container or an sbx sandbox.
49+type RuntimeEntry struct {
50+ Type string `yaml:"type"`
51+ Container string `yaml:"container,omitempty"`
52+ Sandbox string `yaml:"sandbox,omitempty"`
53+ WorkspacePath string `yaml:"workspacePath,omitempty"`
4154 }
4255
4356 // AppendAgent returns content with entry added at the end of the agents list.
@@ -79,8 +92,37 @@ func AppendAgent(content []byte, entry AgentEntry) ([]byte, error) {
7992 if err := encoder.Close(); err != nil {
8093 return nil, err
8194 }
82- if _, err := Parse(buffer.Bytes(), "/"); err != nil {
95+ updated := unescapeLongCharacters(buffer.Bytes())
96+ if _, err := Parse(updated, "/"); err != nil {
8397 return nil, fmt.Errorf("the agent cannot be added: %w", err)
8498 }
85- return buffer.Bytes(), nil
99+ return updated, nil
100+}
101+
102+// unescapeLongCharacters writes back the characters the YAML encoder escapes
103+// as \UXXXXXXXX in double-quoted strings, emoji above all: the file is read
104+// by people, and an icon must stay "🤖", not "\U0001F916". An escaped
105+// backslash ("\\") is copied as it is, so a literal "\U…" text is kept.
106+//
107+// unescapeLongCharacters([]byte(`icon: "\U0001F916"`)) // icon: "🤖"
108+func unescapeLongCharacters(content []byte) []byte {
109+ var result bytes.Buffer
110+ for index := 0; index < len(content); index++ {
111+ if content[index] != '\\' || index+1 >= len(content) {
112+ result.WriteByte(content[index])
113+ continue
114+ }
115+ next := content[index+1]
116+ if next == 'U' && index+10 <= len(content) {
117+ if code, err := strconv.ParseUint(string(content[index+2:index+10]), 16, 32); err == nil && utf8.ValidRune(rune(code)) {
118+ result.WriteRune(rune(code))
119+ index += 9
120+ continue
121+ }
122+ }
123+ result.WriteByte('\\')
124+ result.WriteByte(next)
125+ index++
126+ }
127+ return result.Bytes()
86128 }
@@ -7,6 +7,8 @@ import (
7 "errors"7 "errors"
8 "fmt"8 "fmt"
9 "path/filepath"9 "path/filepath"
10+ "strconv"
11+ "unicode/utf8"
10 12
11 "go.yaml.in/yaml/v3"13 "go.yaml.in/yaml/v3"
12 )14 )
@@ -38,6 +40,17 @@ type AgentEntry struct {
38 Args []string `yaml:"args,omitempty,flow"`40 Args []string `yaml:"args,omitempty,flow"`
39 Env map[string]string `yaml:"env,omitempty"`41 Env map[string]string `yaml:"env,omitempty"`
40 Workspace string `yaml:"workspace,omitempty"`42 Workspace string `yaml:"workspace,omitempty"`
43+ // Runtime is left out for an agent that runs on this computer.
44+ Runtime *RuntimeEntry `yaml:"runtime,omitempty"`
45+}
46+
47+// RuntimeEntry is the runtime of an AgentEntry: where its files and its
48+// terminal are when the agent runs in a container or an sbx sandbox.
49+type RuntimeEntry struct {
50+ Type string `yaml:"type"`
51+ Container string `yaml:"container,omitempty"`
52+ Sandbox string `yaml:"sandbox,omitempty"`
53+ WorkspacePath string `yaml:"workspacePath,omitempty"`
41 }54 }
42 55
43 // AppendAgent returns content with entry added at the end of the agents list.56 // AppendAgent returns content with entry added at the end of the agents list.
@@ -79,8 +92,37 @@ func AppendAgent(content []byte, entry AgentEntry) ([]byte, error) {
79 if err := encoder.Close(); err != nil {92 if err := encoder.Close(); err != nil {
80 return nil, err93 return nil, err
81 }94 }
82- if _, err := Parse(buffer.Bytes(), "/"); err != nil {95+ updated := unescapeLongCharacters(buffer.Bytes())
96+ if _, err := Parse(updated, "/"); err != nil {
83 return nil, fmt.Errorf("the agent cannot be added: %w", err)97 return nil, fmt.Errorf("the agent cannot be added: %w", err)
84 }98 }
85- return buffer.Bytes(), nil99+ return updated, nil
100+}
101+
102+// unescapeLongCharacters writes back the characters the YAML encoder escapes
103+// as \UXXXXXXXX in double-quoted strings, emoji above all: the file is read
104+// by people, and an icon must stay "🤖", not "\U0001F916". An escaped
105+// backslash ("\\") is copied as it is, so a literal "\U…" text is kept.
106+//
107+// unescapeLongCharacters([]byte(`icon: "\U0001F916"`)) // icon: "🤖"
108+func unescapeLongCharacters(content []byte) []byte {
109+ var result bytes.Buffer
110+ for index := 0; index < len(content); index++ {
111+ if content[index] != '\\' || index+1 >= len(content) {
112+ result.WriteByte(content[index])
113+ continue
114+ }
115+ next := content[index+1]
116+ if next == 'U' && index+10 <= len(content) {
117+ if code, err := strconv.ParseUint(string(content[index+2:index+10]), 16, 32); err == nil && utf8.ValidRune(rune(code)) {
118+ result.WriteRune(rune(code))
119+ index += 9
120+ continue
121+ }
122+ }
123+ result.WriteByte('\\')
124+ result.WriteByte(next)
125+ index++
126+ }
127+ return result.Bytes()
86 }128 }
added internal/shellpath/shellpath.go +99 -0
new file mode 100644
@@ -0,0 +1,99 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Package shellpath gives Sidekicks the PATH of the user's terminal.
4+//
5+// An application opened from the Finder (or a desktop launcher) does not
6+// get the PATH of the user's shell: on macOS it is only
7+// /usr/bin:/bin:/usr/sbin:/sbin, so mm, docker or sbx installed with
8+// Homebrew or in ~/.local/bin cannot be found. At startup, Sidekicks asks the
9+// user's shell for its PATH and adds it to its own.
10+//
11+// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
12+// defer cancel()
13+// if err := shellpath.Apply(ctx); err != nil { log.Print(err) }
14+package shellpath
15+
16+import (
17+ "bytes"
18+ "context"
19+ "errors"
20+ "os"
21+ "os/exec"
22+ "path/filepath"
23+ "runtime"
24+ "strings"
25+)
26+
27+// The PATH is printed between two markers: a shell configuration may print
28+// other things (a greeting, a warning) before or after it.
29+const (
30+ startMarker = "__SIDEKICKS_PATH_START__"
31+ endMarker = "__SIDEKICKS_PATH_END__"
32+)
33+
34+// FromShell runs shell as an interactive login shell, so that it reads the
35+// same files as a terminal (.zprofile and .zshrc for zsh), and returns its
36+// PATH.
37+//
38+// path, err := shellpath.FromShell(ctx, "/bin/zsh")
39+func FromShell(ctx context.Context, shell string) (string, error) {
40+ command := exec.CommandContext(ctx, shell, "-i", "-l", "-c", `printf '%s%s%s' "`+startMarker+`" "$PATH" "`+endMarker+`"`)
41+ command.Stdin = nil
42+ var output bytes.Buffer
43+ command.Stdout = &output
44+ if err := command.Run(); err != nil && !strings.Contains(output.String(), endMarker) {
45+ return "", err
46+ }
47+ text := output.String()
48+ start := strings.LastIndex(text, startMarker)
49+ if start < 0 {
50+ return "", errors.New("the shell did not print its PATH")
51+ }
52+ text = text[start+len(startMarker):]
53+ end := strings.Index(text, endMarker)
54+ if end < 0 {
55+ return "", errors.New("the shell did not print its PATH")
56+ }
57+ return text[:end], nil
58+}
59+
60+// Merge returns the folders of first, then those of second that first does
61+// not have, without empty entries. The shell's PATH comes first, as in a
62+// terminal.
63+//
64+// shellpath.Merge("/opt/homebrew/bin:/usr/bin", "/usr/bin:/bin") // "/opt/homebrew/bin:/usr/bin:/bin"
65+func Merge(first, second string) string {
66+ seen := map[string]bool{}
67+ var folders []string
68+ for _, list := range []string{first, second} {
69+ for _, folder := range filepath.SplitList(list) {
70+ if folder == "" || seen[folder] {
71+ continue
72+ }
73+ seen[folder] = true
74+ folders = append(folders, folder)
75+ }
76+ }
77+ return strings.Join(folders, string(os.PathListSeparator))
78+}
79+
80+// Apply adds the PATH of the user's shell ($SHELL, else zsh on macOS and sh
81+// elsewhere) to the PATH of this process, which the agents inherit. It does
82+// nothing on Windows. On failure the PATH is left unchanged.
83+func Apply(ctx context.Context) error {
84+ if runtime.GOOS == "windows" {
85+ return nil
86+ }
87+ shell := os.Getenv("SHELL")
88+ if shell == "" {
89+ shell = "/bin/sh"
90+ if runtime.GOOS == "darwin" {
91+ shell = "/bin/zsh"
92+ }
93+ }
94+ path, err := FromShell(ctx, shell)
95+ if err != nil {
96+ return err
97+ }
98+ return os.Setenv("PATH", Merge(path, os.Getenv("PATH")))
99+}
new file mode 100644
@@ -0,0 +1,99 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+// Package shellpath gives Sidekicks the PATH of the user's terminal.
4+//
5+// An application opened from the Finder (or a desktop launcher) does not
6+// get the PATH of the user's shell: on macOS it is only
7+// /usr/bin:/bin:/usr/sbin:/sbin, so mm, docker or sbx installed with
8+// Homebrew or in ~/.local/bin cannot be found. At startup, Sidekicks asks the
9+// user's shell for its PATH and adds it to its own.
10+//
11+// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
12+// defer cancel()
13+// if err := shellpath.Apply(ctx); err != nil { log.Print(err) }
14+package shellpath
15+
16+import (
17+ "bytes"
18+ "context"
19+ "errors"
20+ "os"
21+ "os/exec"
22+ "path/filepath"
23+ "runtime"
24+ "strings"
25+)
26+
27+// The PATH is printed between two markers: a shell configuration may print
28+// other things (a greeting, a warning) before or after it.
29+const (
30+ startMarker = "__SIDEKICKS_PATH_START__"
31+ endMarker = "__SIDEKICKS_PATH_END__"
32+)
33+
34+// FromShell runs shell as an interactive login shell, so that it reads the
35+// same files as a terminal (.zprofile and .zshrc for zsh), and returns its
36+// PATH.
37+//
38+// path, err := shellpath.FromShell(ctx, "/bin/zsh")
39+func FromShell(ctx context.Context, shell string) (string, error) {
40+ command := exec.CommandContext(ctx, shell, "-i", "-l", "-c", `printf '%s%s%s' "`+startMarker+`" "$PATH" "`+endMarker+`"`)
41+ command.Stdin = nil
42+ var output bytes.Buffer
43+ command.Stdout = &output
44+ if err := command.Run(); err != nil && !strings.Contains(output.String(), endMarker) {
45+ return "", err
46+ }
47+ text := output.String()
48+ start := strings.LastIndex(text, startMarker)
49+ if start < 0 {
50+ return "", errors.New("the shell did not print its PATH")
51+ }
52+ text = text[start+len(startMarker):]
53+ end := strings.Index(text, endMarker)
54+ if end < 0 {
55+ return "", errors.New("the shell did not print its PATH")
56+ }
57+ return text[:end], nil
58+}
59+
60+// Merge returns the folders of first, then those of second that first does
61+// not have, without empty entries. The shell's PATH comes first, as in a
62+// terminal.
63+//
64+// shellpath.Merge("/opt/homebrew/bin:/usr/bin", "/usr/bin:/bin") // "/opt/homebrew/bin:/usr/bin:/bin"
65+func Merge(first, second string) string {
66+ seen := map[string]bool{}
67+ var folders []string
68+ for _, list := range []string{first, second} {
69+ for _, folder := range filepath.SplitList(list) {
70+ if folder == "" || seen[folder] {
71+ continue
72+ }
73+ seen[folder] = true
74+ folders = append(folders, folder)
75+ }
76+ }
77+ return strings.Join(folders, string(os.PathListSeparator))
78+}
79+
80+// Apply adds the PATH of the user's shell ($SHELL, else zsh on macOS and sh
81+// elsewhere) to the PATH of this process, which the agents inherit. It does
82+// nothing on Windows. On failure the PATH is left unchanged.
83+func Apply(ctx context.Context) error {
84+ if runtime.GOOS == "windows" {
85+ return nil
86+ }
87+ shell := os.Getenv("SHELL")
88+ if shell == "" {
89+ shell = "/bin/sh"
90+ if runtime.GOOS == "darwin" {
91+ shell = "/bin/zsh"
92+ }
93+ }
94+ path, err := FromShell(ctx, shell)
95+ if err != nil {
96+ return err
97+ }
98+ return os.Setenv("PATH", Merge(path, os.Getenv("PATH")))
99+}
added internal/shellpath/shellpath_test.go +71 -0
new file mode 100644
@@ -0,0 +1,71 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package shellpath_test
4+
5+import (
6+ "context"
7+ "os"
8+ "path/filepath"
9+ "testing"
10+ "time"
11+
12+ "git.rickub.com/bots-garden/sidekicks/internal/shellpath"
13+)
14+
15+// fakeShell writes a shell that prints noise around what it is asked to run,
16+// with PATH set to path, like a chatty .zshrc would.
17+func fakeShell(t *testing.T, path string) string {
18+ t.Helper()
19+ shell := filepath.Join(t.TempDir(), "fake-shell")
20+ script := "#!/bin/sh\necho 'Welcome!'\nshift 3\nPATH='" + path + "' /bin/sh -c \"$1\"\necho 'bye'\n"
21+ if err := os.WriteFile(shell, []byte(script), 0o755); err != nil {
22+ t.Fatal(err)
23+ }
24+ return shell
25+}
26+
27+func TestFromShellReadsThePathBetweenTheNoise(t *testing.T) {
28+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
29+ defer cancel()
30+ got, err := shellpath.FromShell(ctx, fakeShell(t, "/opt/homebrew/bin:/usr/bin"))
31+ if err != nil || got != "/opt/homebrew/bin:/usr/bin" {
32+ t.Fatalf("got %q %v", got, err)
33+ }
34+}
35+
36+func TestFromShellFailsWithoutAPath(t *testing.T) {
37+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
38+ defer cancel()
39+ if _, err := shellpath.FromShell(ctx, "/bin/false"); err == nil {
40+ t.Fatal("a shell that prints nothing is an error")
41+ }
42+ if _, err := shellpath.FromShell(ctx, filepath.Join(t.TempDir(), "missing")); err == nil {
43+ t.Fatal("a missing shell is an error")
44+ }
45+}
46+
47+func TestMergePutsTheShellFirstWithoutDuplicates(t *testing.T) {
48+ cases := []struct{ first, second, want string }{
49+ {"/opt/homebrew/bin:/usr/bin", "/usr/bin:/bin", "/opt/homebrew/bin:/usr/bin:/bin"},
50+ {"", "/usr/bin:/bin", "/usr/bin:/bin"},
51+ {"/a::/a", "", "/a"},
52+ }
53+ for _, c := range cases {
54+ if got := shellpath.Merge(c.first, c.second); got != c.want {
55+ t.Errorf("Merge(%q, %q) = %q, want %q", c.first, c.second, got, c.want)
56+ }
57+ }
58+}
59+
60+func TestApplyAddsTheShellsPathToTheProcess(t *testing.T) {
61+ t.Setenv("SHELL", fakeShell(t, "/from/the/shell:/usr/bin"))
62+ t.Setenv("PATH", "/usr/bin:/bin")
63+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
64+ defer cancel()
65+ if err := shellpath.Apply(ctx); err != nil {
66+ t.Fatal(err)
67+ }
68+ if got := os.Getenv("PATH"); got != "/from/the/shell:/usr/bin:/bin" {
69+ t.Fatalf("PATH = %q", got)
70+ }
71+}
new file mode 100644
@@ -0,0 +1,71 @@
1+// Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+
3+package shellpath_test
4+
5+import (
6+ "context"
7+ "os"
8+ "path/filepath"
9+ "testing"
10+ "time"
11+
12+ "git.rickub.com/bots-garden/sidekicks/internal/shellpath"
13+)
14+
15+// fakeShell writes a shell that prints noise around what it is asked to run,
16+// with PATH set to path, like a chatty .zshrc would.
17+func fakeShell(t *testing.T, path string) string {
18+ t.Helper()
19+ shell := filepath.Join(t.TempDir(), "fake-shell")
20+ script := "#!/bin/sh\necho 'Welcome!'\nshift 3\nPATH='" + path + "' /bin/sh -c \"$1\"\necho 'bye'\n"
21+ if err := os.WriteFile(shell, []byte(script), 0o755); err != nil {
22+ t.Fatal(err)
23+ }
24+ return shell
25+}
26+
27+func TestFromShellReadsThePathBetweenTheNoise(t *testing.T) {
28+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
29+ defer cancel()
30+ got, err := shellpath.FromShell(ctx, fakeShell(t, "/opt/homebrew/bin:/usr/bin"))
31+ if err != nil || got != "/opt/homebrew/bin:/usr/bin" {
32+ t.Fatalf("got %q %v", got, err)
33+ }
34+}
35+
36+func TestFromShellFailsWithoutAPath(t *testing.T) {
37+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
38+ defer cancel()
39+ if _, err := shellpath.FromShell(ctx, "/bin/false"); err == nil {
40+ t.Fatal("a shell that prints nothing is an error")
41+ }
42+ if _, err := shellpath.FromShell(ctx, filepath.Join(t.TempDir(), "missing")); err == nil {
43+ t.Fatal("a missing shell is an error")
44+ }
45+}
46+
47+func TestMergePutsTheShellFirstWithoutDuplicates(t *testing.T) {
48+ cases := []struct{ first, second, want string }{
49+ {"/opt/homebrew/bin:/usr/bin", "/usr/bin:/bin", "/opt/homebrew/bin:/usr/bin:/bin"},
50+ {"", "/usr/bin:/bin", "/usr/bin:/bin"},
51+ {"/a::/a", "", "/a"},
52+ }
53+ for _, c := range cases {
54+ if got := shellpath.Merge(c.first, c.second); got != c.want {
55+ t.Errorf("Merge(%q, %q) = %q, want %q", c.first, c.second, got, c.want)
56+ }
57+ }
58+}
59+
60+func TestApplyAddsTheShellsPathToTheProcess(t *testing.T) {
61+ t.Setenv("SHELL", fakeShell(t, "/from/the/shell:/usr/bin"))
62+ t.Setenv("PATH", "/usr/bin:/bin")
63+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
64+ defer cancel()
65+ if err := shellpath.Apply(ctx); err != nil {
66+ t.Fatal(err)
67+ }
68+ if got := os.Getenv("PATH"); got != "/from/the/shell:/usr/bin:/bin" {
69+ t.Fatalf("PATH = %q", got)
70+ }
71+}
modified main.go +20 -1
@@ -15,12 +15,14 @@ import (
1515 "fmt"
1616 "log"
1717 "os"
18+ "time"
1819
1920 "git.rickub.com/bots-garden/sidekicks/internal/app"
2021 "git.rickub.com/bots-garden/sidekicks/internal/channels"
2122 "git.rickub.com/bots-garden/sidekicks/internal/config"
2223 "git.rickub.com/bots-garden/sidekicks/internal/events"
2324 "git.rickub.com/bots-garden/sidekicks/internal/prefs"
25+ "git.rickub.com/bots-garden/sidekicks/internal/shellpath"
2426 "git.rickub.com/bots-garden/sidekicks/internal/workspace"
2527 "github.com/wailsapp/wails/v3/pkg/application"
2628 )
@@ -54,6 +56,14 @@ func main() {
5456 configPath := flag.String("config", "", "configuration file (default: $"+config.EnvironmentVariable+", then the default location)")
5557 flag.Parse()
5658
59+ // Before the configuration is read: its warnings say whether each
60+ // agent's program can be found, and the agents inherit this PATH.
61+ shellCtx, shellCancel := context.WithTimeout(context.Background(), 5*time.Second)
62+ if err := shellpath.Apply(shellCtx); err != nil {
63+ log.Printf("sidekicks: the PATH of your shell could not be read (%v); programs are looked up in %s", err, os.Getenv("PATH"))
64+ }
65+ shellCancel()
66+
5767 var preferences *prefs.Store
5868 if path, err := prefs.DefaultPath(); err == nil {
5969 preferences = prefs.NewStore(path)
@@ -84,7 +94,16 @@ func main() {
8494 AddFilter("YAML configuration", "*.yaml;*.yml").
8595 PromptForSingleSelection()
8696 }
87- core := app.New(location, app.Options{Emitter: emitter, Preferences: preferences, PickFile: pickFile})
97+ pickFolder := func(folder string) (string, error) {
98+ return wailsApp.Dialog.OpenFile().
99+ SetTitle("Choose the agent's folder").
100+ SetDirectory(folder).
101+ CanChooseFiles(false).
102+ CanChooseDirectories(true).
103+ CanCreateDirectories(true).
104+ PromptForSingleSelection()
105+ }
106+ core := app.New(location, app.Options{Emitter: emitter, Preferences: preferences, PickFile: pickFile, PickFolder: pickFolder})
88107 ctx, cancel := context.WithCancel(context.Background())
89108
90109 wailsApp = application.New(application.Options{
@@ -15,12 +15,14 @@ import (
15 "fmt"15 "fmt"
16 "log"16 "log"
17 "os"17 "os"
18+ "time"
18 19
19 "git.rickub.com/bots-garden/sidekicks/internal/app"20 "git.rickub.com/bots-garden/sidekicks/internal/app"
20 "git.rickub.com/bots-garden/sidekicks/internal/channels"21 "git.rickub.com/bots-garden/sidekicks/internal/channels"
21 "git.rickub.com/bots-garden/sidekicks/internal/config"22 "git.rickub.com/bots-garden/sidekicks/internal/config"
22 "git.rickub.com/bots-garden/sidekicks/internal/events"23 "git.rickub.com/bots-garden/sidekicks/internal/events"
23 "git.rickub.com/bots-garden/sidekicks/internal/prefs"24 "git.rickub.com/bots-garden/sidekicks/internal/prefs"
25+ "git.rickub.com/bots-garden/sidekicks/internal/shellpath"
24 "git.rickub.com/bots-garden/sidekicks/internal/workspace"26 "git.rickub.com/bots-garden/sidekicks/internal/workspace"
25 "github.com/wailsapp/wails/v3/pkg/application"27 "github.com/wailsapp/wails/v3/pkg/application"
26 )28 )
@@ -54,6 +56,14 @@ func main() {
54 configPath := flag.String("config", "", "configuration file (default: $"+config.EnvironmentVariable+", then the default location)")56 configPath := flag.String("config", "", "configuration file (default: $"+config.EnvironmentVariable+", then the default location)")
55 flag.Parse()57 flag.Parse()
56 58
59+ // Before the configuration is read: its warnings say whether each
60+ // agent's program can be found, and the agents inherit this PATH.
61+ shellCtx, shellCancel := context.WithTimeout(context.Background(), 5*time.Second)
62+ if err := shellpath.Apply(shellCtx); err != nil {
63+ log.Printf("sidekicks: the PATH of your shell could not be read (%v); programs are looked up in %s", err, os.Getenv("PATH"))
64+ }
65+ shellCancel()
66+
57 var preferences *prefs.Store67 var preferences *prefs.Store
58 if path, err := prefs.DefaultPath(); err == nil {68 if path, err := prefs.DefaultPath(); err == nil {
59 preferences = prefs.NewStore(path)69 preferences = prefs.NewStore(path)
@@ -84,7 +94,16 @@ func main() {
84 AddFilter("YAML configuration", "*.yaml;*.yml").94 AddFilter("YAML configuration", "*.yaml;*.yml").
85 PromptForSingleSelection()95 PromptForSingleSelection()
86 }96 }
87- core := app.New(location, app.Options{Emitter: emitter, Preferences: preferences, PickFile: pickFile})97+ pickFolder := func(folder string) (string, error) {
98+ return wailsApp.Dialog.OpenFile().
99+ SetTitle("Choose the agent's folder").
100+ SetDirectory(folder).
101+ CanChooseFiles(false).
102+ CanChooseDirectories(true).
103+ CanCreateDirectories(true).
104+ PromptForSingleSelection()
105+ }
106+ core := app.New(location, app.Options{Emitter: emitter, Preferences: preferences, PickFile: pickFile, PickFolder: pickFolder})
88 ctx, cancel := context.WithCancel(context.Background())107 ctx, cancel := context.WithCancel(context.Background())
89 108
90 wailsApp = application.New(application.Options{109 wailsApp = application.New(application.Options{
added scripts/build-release.sh +69 -0
new file mode 100755
@@ -0,0 +1,69 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Builds every release package of Sidekicks on this Mac, into dist/, then
5+# writes dist/SHA256SUMS. The files are then attached by hand to the release
6+# that scripts/tag-release.sh starts (see RELEASING.md).
7+#
8+# scripts/build-release.sh # macOS, Windows amd64, Linux amd64
9+# LINUX_ARCHES="amd64 arm64" scripts/build-release.sh
10+# SKIP="linux" scripts/build-release.sh # skip systems (macos, windows, linux)
11+#
12+# macOS: native (Xcode command line tools). Windows: cross-built (needs
13+# makensis and zip: brew install makensis zip). Linux: in a Docker container
14+# (scripts/linux-packages.Dockerfile, needs Docker running); another
15+# architecture than the Mac's is emulated, hence slower.
16+set -euo pipefail
17+
18+root="$(cd "$(dirname "$0")/.." && pwd)"
19+dist="$root/dist"
20+linux_arches="${LINUX_ARCHES:-amd64}"
21+skip=" ${SKIP:-} "
22+
23+die() { echo "build-release.sh: $*" >&2; exit 1; }
24+step() { printf '\n==> %s\n' "$*"; }
25+skipped() { [[ "$skip" == *" $1 "* ]]; }
26+
27+cd "$root"
28+version="$(scripts/package.sh version)"
29+step "Sidekicks $version"
30+rm -rf "$dist"
31+
32+if ! skipped macos; then
33+ [ "$(uname -s)" = Darwin ] || die "the macOS packages are built on macOS (or SKIP=macos)"
34+ step "macOS (universal)"
35+ scripts/package.sh macos
36+fi
37+
38+if ! skipped windows; then
39+ command -v makensis >/dev/null || die "makensis is missing: brew install makensis (or SKIP=windows)"
40+ step "Windows (amd64)"
41+ scripts/package.sh windows
42+fi
43+
44+if ! skipped linux; then
45+ command -v docker >/dev/null && docker info >/dev/null 2>&1 || die "Docker is not running (or SKIP=linux)"
46+ go_version="$(awk '/^go /{print $2}' go.mod)"
47+ pnpm="$(node -p "require('./frontend/package.json').packageManager")"
48+ for arch in $linux_arches; do
49+ step "Linux ($arch, in Docker)"
50+ image="sidekicks-linux-packages:$arch"
51+ docker build --quiet --platform "linux/$arch" \
52+ --build-arg "GO_VERSION=$go_version" --build-arg "PNPM=$pnpm" \
53+ -t "$image" -f scripts/linux-packages.Dockerfile scripts >/dev/null
54+ # The Go module cache survives from one release to the next.
55+ docker run --rm --platform "linux/$arch" \
56+ -v "$root:/src" -v "sidekicks-gomod-$arch:/root/go/pkg/mod" \
57+ -e "HOST_UID=$(id -u)" -e "HOST_GID=$(id -g)" \
58+ "$image" bash -c 'scripts/package.sh linux && chown -R "$HOST_UID:$HOST_GID" dist bin frontend/dist'
59+ done
60+fi
61+
62+step "Checksums"
63+# Written outside dist/ first, so that the list does not include itself.
64+sums="$(mktemp)"
65+(cd "$dist" && if command -v sha256sum >/dev/null; then sha256sum -- *; else shasum -a 256 -- *; fi) > "$sums"
66+mv "$sums" "$dist/SHA256SUMS"
67+chmod 644 "$dist/SHA256SUMS"
68+cat "$dist/SHA256SUMS"
69+step "Done: attach every file of dist/ to the release v$version (scripts/tag-release.sh creates it)"
new file mode 100755
@@ -0,0 +1,69 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Builds every release package of Sidekicks on this Mac, into dist/, then
5+# writes dist/SHA256SUMS. The files are then attached by hand to the release
6+# that scripts/tag-release.sh starts (see RELEASING.md).
7+#
8+# scripts/build-release.sh # macOS, Windows amd64, Linux amd64
9+# LINUX_ARCHES="amd64 arm64" scripts/build-release.sh
10+# SKIP="linux" scripts/build-release.sh # skip systems (macos, windows, linux)
11+#
12+# macOS: native (Xcode command line tools). Windows: cross-built (needs
13+# makensis and zip: brew install makensis zip). Linux: in a Docker container
14+# (scripts/linux-packages.Dockerfile, needs Docker running); another
15+# architecture than the Mac's is emulated, hence slower.
16+set -euo pipefail
17+
18+root="$(cd "$(dirname "$0")/.." && pwd)"
19+dist="$root/dist"
20+linux_arches="${LINUX_ARCHES:-amd64}"
21+skip=" ${SKIP:-} "
22+
23+die() { echo "build-release.sh: $*" >&2; exit 1; }
24+step() { printf '\n==> %s\n' "$*"; }
25+skipped() { [[ "$skip" == *" $1 "* ]]; }
26+
27+cd "$root"
28+version="$(scripts/package.sh version)"
29+step "Sidekicks $version"
30+rm -rf "$dist"
31+
32+if ! skipped macos; then
33+ [ "$(uname -s)" = Darwin ] || die "the macOS packages are built on macOS (or SKIP=macos)"
34+ step "macOS (universal)"
35+ scripts/package.sh macos
36+fi
37+
38+if ! skipped windows; then
39+ command -v makensis >/dev/null || die "makensis is missing: brew install makensis (or SKIP=windows)"
40+ step "Windows (amd64)"
41+ scripts/package.sh windows
42+fi
43+
44+if ! skipped linux; then
45+ command -v docker >/dev/null && docker info >/dev/null 2>&1 || die "Docker is not running (or SKIP=linux)"
46+ go_version="$(awk '/^go /{print $2}' go.mod)"
47+ pnpm="$(node -p "require('./frontend/package.json').packageManager")"
48+ for arch in $linux_arches; do
49+ step "Linux ($arch, in Docker)"
50+ image="sidekicks-linux-packages:$arch"
51+ docker build --quiet --platform "linux/$arch" \
52+ --build-arg "GO_VERSION=$go_version" --build-arg "PNPM=$pnpm" \
53+ -t "$image" -f scripts/linux-packages.Dockerfile scripts >/dev/null
54+ # The Go module cache survives from one release to the next.
55+ docker run --rm --platform "linux/$arch" \
56+ -v "$root:/src" -v "sidekicks-gomod-$arch:/root/go/pkg/mod" \
57+ -e "HOST_UID=$(id -u)" -e "HOST_GID=$(id -g)" \
58+ "$image" bash -c 'scripts/package.sh linux && chown -R "$HOST_UID:$HOST_GID" dist bin frontend/dist'
59+ done
60+fi
61+
62+step "Checksums"
63+# Written outside dist/ first, so that the list does not include itself.
64+sums="$(mktemp)"
65+(cd "$dist" && if command -v sha256sum >/dev/null; then sha256sum -- *; else shasum -a 256 -- *; fi) > "$sums"
66+mv "$sums" "$dist/SHA256SUMS"
67+chmod 644 "$dist/SHA256SUMS"
68+cat "$dist/SHA256SUMS"
69+step "Done: attach every file of dist/ to the release v$version (scripts/tag-release.sh creates it)"
added scripts/linux-packages.Dockerfile +34 -0
new file mode 100644
@@ -0,0 +1,34 @@
1+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+#
3+# The image scripts/build-release.sh uses to build the Linux packages from
4+# macOS (Wails needs GTK 4 and WebKitGTK 6, which macOS cannot link): the
5+# repository is mounted at /src and `scripts/package.sh linux` runs in it.
6+# ubuntu:26.04, like the CI: GTK >= 4.10. The AppImage's GTK plugin copies
7+# folders that only these packages create: the GTK 4 modules (ibus-gtk4,
8+# input methods) and the image loaders (librsvg2-common, SVG).
9+#
10+# docker build --platform linux/amd64 --build-arg GO_VERSION=1.26.0 --build-arg PNPM=pnpm@12.11.2 \
11+# -t sidekicks-linux-packages:amd64 -f scripts/linux-packages.Dockerfile scripts
12+FROM ubuntu:26.04
13+
14+ARG GO_VERSION
15+ARG PNPM
16+ENV DEBIAN_FRONTEND=noninteractive
17+
18+RUN apt-get update -qq \
19+ && apt-get install -y -qq --no-install-recommends \
20+ ca-certificates curl git nodejs npm build-essential pkg-config \
21+ libgtk-4-dev libwebkitgtk-6.0-dev file rpm zip \
22+ ibus-gtk4 librsvg2-common \
23+ && rm -rf /var/lib/apt/lists/*
24+
25+RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz" | tar -C /usr/local -xz
26+ENV PATH=/usr/local/go/bin:/usr/local/bin:$PATH
27+
28+RUN npm install -g "${PNPM}" \
29+ && GOBIN=/usr/local/bin go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0 \
30+ && go clean -cache -modcache \
31+ && npm cache clean --force \
32+ && git config --global --add safe.directory '*'
33+
34+WORKDIR /src
new file mode 100644
@@ -0,0 +1,34 @@
1+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
2+#
3+# The image scripts/build-release.sh uses to build the Linux packages from
4+# macOS (Wails needs GTK 4 and WebKitGTK 6, which macOS cannot link): the
5+# repository is mounted at /src and `scripts/package.sh linux` runs in it.
6+# ubuntu:26.04, like the CI: GTK >= 4.10. The AppImage's GTK plugin copies
7+# folders that only these packages create: the GTK 4 modules (ibus-gtk4,
8+# input methods) and the image loaders (librsvg2-common, SVG).
9+#
10+# docker build --platform linux/amd64 --build-arg GO_VERSION=1.26.0 --build-arg PNPM=pnpm@12.11.2 \
11+# -t sidekicks-linux-packages:amd64 -f scripts/linux-packages.Dockerfile scripts
12+FROM ubuntu:26.04
13+
14+ARG GO_VERSION
15+ARG PNPM
16+ENV DEBIAN_FRONTEND=noninteractive
17+
18+RUN apt-get update -qq \
19+ && apt-get install -y -qq --no-install-recommends \
20+ ca-certificates curl git nodejs npm build-essential pkg-config \
21+ libgtk-4-dev libwebkitgtk-6.0-dev file rpm zip \
22+ ibus-gtk4 librsvg2-common \
23+ && rm -rf /var/lib/apt/lists/*
24+
25+RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz" | tar -C /usr/local -xz
26+ENV PATH=/usr/local/go/bin:/usr/local/bin:$PATH
27+
28+RUN npm install -g "${PNPM}" \
29+ && GOBIN=/usr/local/bin go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0 \
30+ && go clean -cache -modcache \
31+ && npm cache clean --force \
32+ && git config --global --add safe.directory '*'
33+
34+WORKDIR /src
added scripts/package.sh +102 -0
new file mode 100755
@@ -0,0 +1,102 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Builds the Sidekicks release packages into dist/, named with the version
5+# of build/config.yml, the system and the architecture.
6+#
7+# scripts/package.sh linux # .deb, .rpm, .AppImage and .tar.gz for this machine's architecture
8+# scripts/package.sh windows # installer (NSIS) and .zip for Windows amd64, cross-built from Linux or macOS
9+# scripts/package.sh macos # .dmg and .zip of the universal .app (arm64 + amd64), on macOS only
10+# scripts/package.sh names linux arm64 # prints the file names, builds nothing
11+#
12+# Linux needs GTK 4 and WebKitGTK 6 dev packages, plus file (AppImage) and
13+# rpm; Windows needs makensis (nsis); macOS needs the Xcode command line tools.
14+set -euo pipefail
15+
16+root="$(cd "$(dirname "$0")/.." && pwd)"
17+dist="$root/dist"
18+
19+die() { echo "package.sh: $*" >&2; exit 1; }
20+
21+# The version of build/config.yml (info.version).
22+version() {
23+ awk -F'"' '/^ version:/ { print $2; exit }' "$root/build/config.yml"
24+}
25+
26+# names <system> <arch>: the files a build produces, one per line.
27+names() {
28+ local system="$1" arch="$2" v
29+ v="$(version)"
30+ case "$system" in
31+ linux)
32+ local appimage_arch="$arch"
33+ [ "$arch" = amd64 ] && appimage_arch=x86_64
34+ [ "$arch" = arm64 ] && appimage_arch=aarch64
35+ echo "sidekicks_${v}_linux_${arch}.deb"
36+ echo "sidekicks_${v}_linux_${arch}.rpm"
37+ echo "Sidekicks-${v}-${appimage_arch}.AppImage"
38+ echo "sidekicks_${v}_linux_${arch}.tar.gz"
39+ ;;
40+ windows)
41+ echo "Sidekicks-${v}-windows-${arch}-installer.exe"
42+ echo "sidekicks_${v}_windows_${arch}.zip"
43+ ;;
44+ macos)
45+ echo "Sidekicks-${v}-macos-universal.dmg"
46+ echo "Sidekicks-${v}-macos-universal.zip"
47+ ;;
48+ *) die "unknown system: $system (linux, windows or macos)" ;;
49+ esac
50+}
51+
52+# The n-th name (1-based) of names.
53+name() { names "$1" "$2" | sed -n "${3}p"; }
54+
55+wails() { (cd "$root" && wails3 task "$@"); }
56+
57+# bin/ is shared between the Mac and Linux (containers, sandbox): a binary
58+# left by the other system is "not an object file" for go build, which then
59+# refuses to overwrite it.
60+forget_previous_binary() { rm -f "$root/bin/sidekicks"; }
61+
62+package_linux() {
63+ local arch
64+ arch="$(go env GOARCH)"
65+ export GOARCH="$arch"
66+ forget_previous_binary
67+ wails linux:create:deb
68+ wails linux:create:rpm
69+ wails linux:create:appimage
70+ mkdir -p "$dist"
71+ mv "$root/bin/sidekicks.deb" "$dist/$(name linux "$arch" 1)"
72+ mv "$root"/bin/sidekicks*.rpm "$dist/$(name linux "$arch" 2)"
73+ mv "$root"/bin/sidekicks-*.AppImage "$dist/$(name linux "$arch" 3)"
74+ tar -czf "$dist/$(name linux "$arch" 4)" -C "$root/bin" sidekicks
75+}
76+
77+package_windows() {
78+ wails windows:create:nsis:installer ARCH=amd64
79+ mkdir -p "$dist"
80+ mv "$root/bin/sidekicks-amd64-installer.exe" "$dist/$(name windows amd64 1)"
81+ (cd "$root/bin" && zip -q -j "$dist/$(name windows amd64 2)" sidekicks.exe)
82+}
83+
84+package_macos() {
85+ [ "$(uname -s)" = Darwin ] || die "the macOS packages are built on macOS"
86+ forget_previous_binary
87+ wails darwin:package:universal
88+ wails darwin:create:dmg
89+ mkdir -p "$dist"
90+ mv "$root/bin/sidekicks.dmg" "$dist/$(name macos universal 1)"
91+ ditto -c -k --keepParent "$root/bin/sidekicks.app" "$dist/$(name macos universal 2)"
92+}
93+
94+case "${1:-}" in
95+ linux) package_linux ;;
96+ windows) package_windows ;;
97+ macos) package_macos ;;
98+ names) names "${2:?system}" "${3:?arch}" ;;
99+ version) version ;;
100+ *) die "usage: package.sh linux|windows|macos|names <system> <arch>|version" ;;
101+esac
102+[ "${1:-}" = names ] || [ "${1:-}" = version ] || ls -la "$dist"
new file mode 100755
@@ -0,0 +1,102 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Builds the Sidekicks release packages into dist/, named with the version
5+# of build/config.yml, the system and the architecture.
6+#
7+# scripts/package.sh linux # .deb, .rpm, .AppImage and .tar.gz for this machine's architecture
8+# scripts/package.sh windows # installer (NSIS) and .zip for Windows amd64, cross-built from Linux or macOS
9+# scripts/package.sh macos # .dmg and .zip of the universal .app (arm64 + amd64), on macOS only
10+# scripts/package.sh names linux arm64 # prints the file names, builds nothing
11+#
12+# Linux needs GTK 4 and WebKitGTK 6 dev packages, plus file (AppImage) and
13+# rpm; Windows needs makensis (nsis); macOS needs the Xcode command line tools.
14+set -euo pipefail
15+
16+root="$(cd "$(dirname "$0")/.." && pwd)"
17+dist="$root/dist"
18+
19+die() { echo "package.sh: $*" >&2; exit 1; }
20+
21+# The version of build/config.yml (info.version).
22+version() {
23+ awk -F'"' '/^ version:/ { print $2; exit }' "$root/build/config.yml"
24+}
25+
26+# names <system> <arch>: the files a build produces, one per line.
27+names() {
28+ local system="$1" arch="$2" v
29+ v="$(version)"
30+ case "$system" in
31+ linux)
32+ local appimage_arch="$arch"
33+ [ "$arch" = amd64 ] && appimage_arch=x86_64
34+ [ "$arch" = arm64 ] && appimage_arch=aarch64
35+ echo "sidekicks_${v}_linux_${arch}.deb"
36+ echo "sidekicks_${v}_linux_${arch}.rpm"
37+ echo "Sidekicks-${v}-${appimage_arch}.AppImage"
38+ echo "sidekicks_${v}_linux_${arch}.tar.gz"
39+ ;;
40+ windows)
41+ echo "Sidekicks-${v}-windows-${arch}-installer.exe"
42+ echo "sidekicks_${v}_windows_${arch}.zip"
43+ ;;
44+ macos)
45+ echo "Sidekicks-${v}-macos-universal.dmg"
46+ echo "Sidekicks-${v}-macos-universal.zip"
47+ ;;
48+ *) die "unknown system: $system (linux, windows or macos)" ;;
49+ esac
50+}
51+
52+# The n-th name (1-based) of names.
53+name() { names "$1" "$2" | sed -n "${3}p"; }
54+
55+wails() { (cd "$root" && wails3 task "$@"); }
56+
57+# bin/ is shared between the Mac and Linux (containers, sandbox): a binary
58+# left by the other system is "not an object file" for go build, which then
59+# refuses to overwrite it.
60+forget_previous_binary() { rm -f "$root/bin/sidekicks"; }
61+
62+package_linux() {
63+ local arch
64+ arch="$(go env GOARCH)"
65+ export GOARCH="$arch"
66+ forget_previous_binary
67+ wails linux:create:deb
68+ wails linux:create:rpm
69+ wails linux:create:appimage
70+ mkdir -p "$dist"
71+ mv "$root/bin/sidekicks.deb" "$dist/$(name linux "$arch" 1)"
72+ mv "$root"/bin/sidekicks*.rpm "$dist/$(name linux "$arch" 2)"
73+ mv "$root"/bin/sidekicks-*.AppImage "$dist/$(name linux "$arch" 3)"
74+ tar -czf "$dist/$(name linux "$arch" 4)" -C "$root/bin" sidekicks
75+}
76+
77+package_windows() {
78+ wails windows:create:nsis:installer ARCH=amd64
79+ mkdir -p "$dist"
80+ mv "$root/bin/sidekicks-amd64-installer.exe" "$dist/$(name windows amd64 1)"
81+ (cd "$root/bin" && zip -q -j "$dist/$(name windows amd64 2)" sidekicks.exe)
82+}
83+
84+package_macos() {
85+ [ "$(uname -s)" = Darwin ] || die "the macOS packages are built on macOS"
86+ forget_previous_binary
87+ wails darwin:package:universal
88+ wails darwin:create:dmg
89+ mkdir -p "$dist"
90+ mv "$root/bin/sidekicks.dmg" "$dist/$(name macos universal 1)"
91+ ditto -c -k --keepParent "$root/bin/sidekicks.app" "$dist/$(name macos universal 2)"
92+}
93+
94+case "${1:-}" in
95+ linux) package_linux ;;
96+ windows) package_windows ;;
97+ macos) package_macos ;;
98+ names) names "${2:?system}" "${3:?arch}" ;;
99+ version) version ;;
100+ *) die "usage: package.sh linux|windows|macos|names <system> <arch>|version" ;;
101+esac
102+[ "${1:-}" = names ] || [ "${1:-}" = version ] || ls -la "$dist"
added scripts/package_test.sh +50 -0
new file mode 100755
@@ -0,0 +1,50 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tests for package.sh: the version and the names of the release files
5+# (the builds themselves are checked by the release workflow).
6+#
7+# Usage: scripts/package_test.sh
8+
9+set -eu
10+
11+script="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/package.sh"
12+failures=0
13+
14+expect() {
15+ local name="$1" expected="$2"
16+ shift 2
17+ local output status=0
18+ output="$("$script" "$@" 2>&1)" || status=$?
19+ if [ "$status" -eq 0 ] && [ "$output" = "$expected" ]; then
20+ echo "ok $name"
21+ else
22+ echo "FAIL $name (status $status)"
23+ printf 'expected:\n%s\ngot:\n%s\n' "$expected" "$output" | sed 's/^/ /'
24+ failures=$((failures + 1))
25+ fi
26+}
27+
28+version="$("$script" version)"
29+if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "ok the version comes from build/config.yml ($version)"; else echo "FAIL version: '$version'"; failures=$((failures + 1)); fi
30+
31+expect "linux names on arm64 (AppImage uses aarch64)" "sidekicks_${version}_linux_arm64.deb
32+sidekicks_${version}_linux_arm64.rpm
33+Sidekicks-${version}-aarch64.AppImage
34+sidekicks_${version}_linux_arm64.tar.gz" names linux arm64
35+
36+expect "linux names on amd64 (AppImage uses x86_64)" "sidekicks_${version}_linux_amd64.deb
37+sidekicks_${version}_linux_amd64.rpm
38+Sidekicks-${version}-x86_64.AppImage
39+sidekicks_${version}_linux_amd64.tar.gz" names linux amd64
40+
41+expect "windows names" "Sidekicks-${version}-windows-amd64-installer.exe
42+sidekicks_${version}_windows_amd64.zip" names windows amd64
43+
44+expect "macos names" "Sidekicks-${version}-macos-universal.dmg
45+Sidekicks-${version}-macos-universal.zip" names macos universal
46+
47+if "$script" names plan9 386 >/dev/null 2>&1; then echo "FAIL an unknown system is refused"; failures=$((failures + 1)); else echo "ok an unknown system is refused"; fi
48+
49+if [ "$failures" -gt 0 ]; then echo "$failures failed"; exit 1; fi
50+echo "all package tests passed"
new file mode 100755
@@ -0,0 +1,50 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tests for package.sh: the version and the names of the release files
5+# (the builds themselves are checked by the release workflow).
6+#
7+# Usage: scripts/package_test.sh
8+
9+set -eu
10+
11+script="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/package.sh"
12+failures=0
13+
14+expect() {
15+ local name="$1" expected="$2"
16+ shift 2
17+ local output status=0
18+ output="$("$script" "$@" 2>&1)" || status=$?
19+ if [ "$status" -eq 0 ] && [ "$output" = "$expected" ]; then
20+ echo "ok $name"
21+ else
22+ echo "FAIL $name (status $status)"
23+ printf 'expected:\n%s\ngot:\n%s\n' "$expected" "$output" | sed 's/^/ /'
24+ failures=$((failures + 1))
25+ fi
26+}
27+
28+version="$("$script" version)"
29+if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "ok the version comes from build/config.yml ($version)"; else echo "FAIL version: '$version'"; failures=$((failures + 1)); fi
30+
31+expect "linux names on arm64 (AppImage uses aarch64)" "sidekicks_${version}_linux_arm64.deb
32+sidekicks_${version}_linux_arm64.rpm
33+Sidekicks-${version}-aarch64.AppImage
34+sidekicks_${version}_linux_arm64.tar.gz" names linux arm64
35+
36+expect "linux names on amd64 (AppImage uses x86_64)" "sidekicks_${version}_linux_amd64.deb
37+sidekicks_${version}_linux_amd64.rpm
38+Sidekicks-${version}-x86_64.AppImage
39+sidekicks_${version}_linux_amd64.tar.gz" names linux amd64
40+
41+expect "windows names" "Sidekicks-${version}-windows-amd64-installer.exe
42+sidekicks_${version}_windows_amd64.zip" names windows amd64
43+
44+expect "macos names" "Sidekicks-${version}-macos-universal.dmg
45+Sidekicks-${version}-macos-universal.zip" names macos universal
46+
47+if "$script" names plan9 386 >/dev/null 2>&1; then echo "FAIL an unknown system is refused"; failures=$((failures + 1)); else echo "ok an unknown system is refused"; fi
48+
49+if [ "$failures" -gt 0 ]; then echo "$failures failed"; exit 1; fi
50+echo "all package tests passed"
added scripts/tag-release.sh +55 -0
new file mode 100755
@@ -0,0 +1,55 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tags the release of the version of build/config.yml (v<version>) on main
5+# and pushes the tag: the Release workflow then creates the release on
6+# Rickub, and the files of dist/ are attached to it by hand (RELEASING.md).
7+#
8+# scripts/tag-release.sh # checks, tags, pushes to origin
9+# scripts/tag-release.sh --no-push # checks and tags locally only
10+# scripts/tag-release.sh --remote fork
11+#
12+# Refuses when the working tree has changes, when not on main, when main is
13+# not the same as on the remote, or when the tag exists. Warns when dist/
14+# misses a package of this version (scripts/build-release.sh builds them).
15+set -euo pipefail
16+
17+root="$(cd "$(dirname "$0")/.." && pwd)"
18+remote=origin
19+push=true
20+
21+while [ $# -gt 0 ]; do
22+ case "$1" in
23+ --remote) remote="${2:?--remote needs a name}"; shift 2 ;;
24+ --no-push) push=false; shift ;;
25+ *) echo "tag-release.sh: unknown option $1" >&2; exit 2 ;;
26+ esac
27+done
28+
29+die() { echo "tag-release.sh: $*" >&2; exit 1; }
30+
31+cd "$root"
32+version="$(scripts/package.sh version)"
33+tag="v$version"
34+
35+[ -z "$(git status --porcelain --untracked-files=no)" ] || die "the working tree has uncommitted changes: commit them first"
36+[ "$(git branch --show-current)" = main ] || die "releases are tagged on main (current branch: $(git branch --show-current))"
37+git fetch --quiet --tags "$remote" main
38+[ "$(git rev-parse HEAD)" = "$(git rev-parse "$remote/main")" ] || die "main differs from $remote/main: pull or push first"
39+if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
40+ die "the tag $tag already exists: raise info.version in build/config.yml"
41+fi
42+
43+missing=""
44+for name in $(scripts/package.sh names macos universal) $(scripts/package.sh names windows amd64) $(scripts/package.sh names linux amd64) SHA256SUMS; do
45+ [ -f "dist/$name" ] || missing="$missing $name"
46+done
47+[ -z "$missing" ] || echo "tag-release.sh: warning: dist/ has no$missing (scripts/build-release.sh builds them)" >&2
48+
49+git tag -a "$tag" -m "Sidekicks $version"
50+if [ "$push" = false ]; then
51+ echo "Tagged $tag locally (not pushed)."
52+ exit 0
53+fi
54+git push "$remote" "$tag"
55+echo "Pushed $tag: the Release workflow creates the release; then attach the files of dist/ to it."
new file mode 100755
@@ -0,0 +1,55 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tags the release of the version of build/config.yml (v<version>) on main
5+# and pushes the tag: the Release workflow then creates the release on
6+# Rickub, and the files of dist/ are attached to it by hand (RELEASING.md).
7+#
8+# scripts/tag-release.sh # checks, tags, pushes to origin
9+# scripts/tag-release.sh --no-push # checks and tags locally only
10+# scripts/tag-release.sh --remote fork
11+#
12+# Refuses when the working tree has changes, when not on main, when main is
13+# not the same as on the remote, or when the tag exists. Warns when dist/
14+# misses a package of this version (scripts/build-release.sh builds them).
15+set -euo pipefail
16+
17+root="$(cd "$(dirname "$0")/.." && pwd)"
18+remote=origin
19+push=true
20+
21+while [ $# -gt 0 ]; do
22+ case "$1" in
23+ --remote) remote="${2:?--remote needs a name}"; shift 2 ;;
24+ --no-push) push=false; shift ;;
25+ *) echo "tag-release.sh: unknown option $1" >&2; exit 2 ;;
26+ esac
27+done
28+
29+die() { echo "tag-release.sh: $*" >&2; exit 1; }
30+
31+cd "$root"
32+version="$(scripts/package.sh version)"
33+tag="v$version"
34+
35+[ -z "$(git status --porcelain --untracked-files=no)" ] || die "the working tree has uncommitted changes: commit them first"
36+[ "$(git branch --show-current)" = main ] || die "releases are tagged on main (current branch: $(git branch --show-current))"
37+git fetch --quiet --tags "$remote" main
38+[ "$(git rev-parse HEAD)" = "$(git rev-parse "$remote/main")" ] || die "main differs from $remote/main: pull or push first"
39+if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
40+ die "the tag $tag already exists: raise info.version in build/config.yml"
41+fi
42+
43+missing=""
44+for name in $(scripts/package.sh names macos universal) $(scripts/package.sh names windows amd64) $(scripts/package.sh names linux amd64) SHA256SUMS; do
45+ [ -f "dist/$name" ] || missing="$missing $name"
46+done
47+[ -z "$missing" ] || echo "tag-release.sh: warning: dist/ has no$missing (scripts/build-release.sh builds them)" >&2
48+
49+git tag -a "$tag" -m "Sidekicks $version"
50+if [ "$push" = false ]; then
51+ echo "Tagged $tag locally (not pushed)."
52+ exit 0
53+fi
54+git push "$remote" "$tag"
55+echo "Pushed $tag: the Release workflow creates the release; then attach the files of dist/ to it."
added scripts/tag-release_test.sh +67 -0
new file mode 100755
@@ -0,0 +1,67 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tests for tag-release.sh, on a throwaway clone of a bare repository.
5+#
6+# Usage: scripts/tag-release_test.sh
7+
8+set -eu
9+
10+here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11+failures=0
12+work="$(mktemp -d)"
13+trap 'rm -rf "$work"' EXIT
14+
15+ok() { echo "ok $1"; }
16+fail() { echo "FAIL $1"; failures=$((failures + 1)); }
17+export GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.com GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.com
18+
19+# expect <name> <0|1> <command…>: the script's success or failure.
20+expect() {
21+ local name="$1" want="$2"
22+ shift 2
23+ if (cd "$work/repo" && "$@") >"$work/out" 2>&1; then got=0; else got=1; fi
24+ if [ "$got" = "$want" ]; then ok "$name"; else fail "$name"; sed 's/^/ /' "$work/out"; fi
25+}
26+
27+git init -q --bare -b main "$work/remote.git"
28+git init -q -b main "$work/repo"
29+mkdir -p "$work/repo/scripts" "$work/repo/build"
30+cp "$here/package.sh" "$here/tag-release.sh" "$work/repo/scripts/"
31+printf 'info:\n version: "1.2.3"\n' > "$work/repo/build/config.yml"
32+printf 'dist/\n' > "$work/repo/.gitignore"
33+git -C "$work/repo" add -A
34+git -C "$work/repo" commit -q -m source
35+git -C "$work/repo" remote add origin "$work/remote.git"
36+git -C "$work/repo" push -q origin main
37+
38+printf 'changed\n' >> "$work/repo/build/config.yml"
39+expect "refuses uncommitted changes" 1 scripts/tag-release.sh
40+git -C "$work/repo" checkout -q -- build/config.yml
41+
42+git -C "$work/repo" checkout -q -b feature
43+expect "refuses another branch than main" 1 scripts/tag-release.sh
44+git -C "$work/repo" checkout -q main
45+
46+git -C "$work/repo" commit -q --allow-empty -m "not pushed"
47+expect "refuses a main that differs from the remote" 1 scripts/tag-release.sh
48+git -C "$work/repo" push -q origin main
49+
50+expect "tags and pushes, warning about missing packages" 0 scripts/tag-release.sh
51+grep -q "warning: dist/ has no" "$work/out" && ok "says which packages are missing" || fail "says which packages are missing"
52+[ "$(git -C "$work/remote.git" tag)" = v1.2.3 ] && ok "the tag v1.2.3 is on the remote" || fail "the tag v1.2.3 is on the remote"
53+[ "$(git -C "$work/remote.git" cat-file -t v1.2.3)" = tag ] && ok "the tag is annotated" || fail "the tag is annotated"
54+
55+expect "refuses an existing tag" 1 scripts/tag-release.sh
56+
57+printf 'info:\n version: "1.2.4"\n' > "$work/repo/build/config.yml"
58+git -C "$work/repo" commit -q -am "1.2.4"
59+git -C "$work/repo" push -q origin main
60+mkdir -p "$work/repo/dist"
61+for name in $("$work/repo/scripts/package.sh" names macos universal) $("$work/repo/scripts/package.sh" names windows amd64) $("$work/repo/scripts/package.sh" names linux amd64) SHA256SUMS; do : > "$work/repo/dist/$name"; done
62+expect "--no-push tags locally only" 0 scripts/tag-release.sh --no-push
63+grep -q warning "$work/out" && fail "no warning when dist/ is complete" || ok "no warning when dist/ is complete"
64+git -C "$work/repo" rev-parse -q --verify refs/tags/v1.2.4 >/dev/null && [ -z "$(git -C "$work/remote.git" tag -l v1.2.4)" ] && ok "the local tag is not pushed" || fail "the local tag is not pushed"
65+
66+if [ "$failures" -gt 0 ]; then echo "$failures failed"; exit 1; fi
67+echo "all tag-release tests passed"
new file mode 100755
@@ -0,0 +1,67 @@
1+#!/usr/bin/env bash
2+# Copyright (c) 2026 Philippe Charrière — Sidekicks Source-Available License 1.0, see LICENSE
3+#
4+# Tests for tag-release.sh, on a throwaway clone of a bare repository.
5+#
6+# Usage: scripts/tag-release_test.sh
7+
8+set -eu
9+
10+here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11+failures=0
12+work="$(mktemp -d)"
13+trap 'rm -rf "$work"' EXIT
14+
15+ok() { echo "ok $1"; }
16+fail() { echo "FAIL $1"; failures=$((failures + 1)); }
17+export GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.com GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.com
18+
19+# expect <name> <0|1> <command…>: the script's success or failure.
20+expect() {
21+ local name="$1" want="$2"
22+ shift 2
23+ if (cd "$work/repo" && "$@") >"$work/out" 2>&1; then got=0; else got=1; fi
24+ if [ "$got" = "$want" ]; then ok "$name"; else fail "$name"; sed 's/^/ /' "$work/out"; fi
25+}
26+
27+git init -q --bare -b main "$work/remote.git"
28+git init -q -b main "$work/repo"
29+mkdir -p "$work/repo/scripts" "$work/repo/build"
30+cp "$here/package.sh" "$here/tag-release.sh" "$work/repo/scripts/"
31+printf 'info:\n version: "1.2.3"\n' > "$work/repo/build/config.yml"
32+printf 'dist/\n' > "$work/repo/.gitignore"
33+git -C "$work/repo" add -A
34+git -C "$work/repo" commit -q -m source
35+git -C "$work/repo" remote add origin "$work/remote.git"
36+git -C "$work/repo" push -q origin main
37+
38+printf 'changed\n' >> "$work/repo/build/config.yml"
39+expect "refuses uncommitted changes" 1 scripts/tag-release.sh
40+git -C "$work/repo" checkout -q -- build/config.yml
41+
42+git -C "$work/repo" checkout -q -b feature
43+expect "refuses another branch than main" 1 scripts/tag-release.sh
44+git -C "$work/repo" checkout -q main
45+
46+git -C "$work/repo" commit -q --allow-empty -m "not pushed"
47+expect "refuses a main that differs from the remote" 1 scripts/tag-release.sh
48+git -C "$work/repo" push -q origin main
49+
50+expect "tags and pushes, warning about missing packages" 0 scripts/tag-release.sh
51+grep -q "warning: dist/ has no" "$work/out" && ok "says which packages are missing" || fail "says which packages are missing"
52+[ "$(git -C "$work/remote.git" tag)" = v1.2.3 ] && ok "the tag v1.2.3 is on the remote" || fail "the tag v1.2.3 is on the remote"
53+[ "$(git -C "$work/remote.git" cat-file -t v1.2.3)" = tag ] && ok "the tag is annotated" || fail "the tag is annotated"
54+
55+expect "refuses an existing tag" 1 scripts/tag-release.sh
56+
57+printf 'info:\n version: "1.2.4"\n' > "$work/repo/build/config.yml"
58+git -C "$work/repo" commit -q -am "1.2.4"
59+git -C "$work/repo" push -q origin main
60+mkdir -p "$work/repo/dist"
61+for name in $("$work/repo/scripts/package.sh" names macos universal) $("$work/repo/scripts/package.sh" names windows amd64) $("$work/repo/scripts/package.sh" names linux amd64) SHA256SUMS; do : > "$work/repo/dist/$name"; done
62+expect "--no-push tags locally only" 0 scripts/tag-release.sh --no-push
63+grep -q warning "$work/out" && fail "no warning when dist/ is complete" || ok "no warning when dist/ is complete"
64+git -C "$work/repo" rev-parse -q --verify refs/tags/v1.2.4 >/dev/null && [ -z "$(git -C "$work/remote.git" tag -l v1.2.4)" ] && ok "the local tag is not pushed" || fail "the local tag is not pushed"
65+
66+if [ "$failures" -gt 0 ]; then echo "$failures failed"; exit 1; fi
67+echo "all tag-release tests passed"