- plugin.json contained duplicate "version" keys ("0.1.0" then "0.2.0"); JSON parsers take the last value so it appeared to work, but this is invalid per spec and a maintenance hazard.
- findActiveExitNode used legacy for-in + hasOwnProperty guard; replaced with Object.keys() for modern, safe iteration (now consistent with parsePeers and the rest of lib.js).
- All 36 unit tests pass after the change.
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- plugin.json: added capabilities + requires; bumped to 0.2.0
- README: corrected "auto-refresh" claim (now documents the intentional on-demand + defensive poll-act-poll); added Implementation notes section covering Proc + I18n + best practices
- test/lib.test.js: updated the manual verification comment (now references Proc + preserved behavior)
- Minor version references in README example + caption
- All changes keep exact toggle semantics and follow repo style
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- Removed import Quickshell.Io + all 4 Process { toggleProcess, copyProcess, exitNodeProcess, statusCheck }
- All one-shot commands now use Proc.runCommand(id, argv, (stdout, exitCode) => {...})
- _pendingAction moved to clean root property (was hacked onto statusCheck object)
- Introduced _runStatusCheck() helper + updated wrappers (toggleTailscale, refreshStatus, setExitNode, copyToClipboard, _runNextCopy)
- Exact same sequencing and defensive logic:
* toggle: set pending → status poll for truth → decide up/down → act → post-act status verify
* copy retry state machine unchanged (just Proc instead of .command/.running)
* exit-node and generic refresh paths identical
- Added explicit comment documenting why the poll-act-poll exists (defensive, not forbidden state per AGENTS.md)
- stderr detail dropped from action errors (per chosen A strategy; generic messages only)
- All new JS follows repo ; + {} style
- I18n wrappers already in place from prior slice
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- header, details, exit node prefix/none, invalid hostname, copied (with .arg), all formatError toasts
- Glyph literals ("×", "↗", "—") left as-is (decorative, not linguistic)
- "tailscalectl" toast titles kept as technical IDs
- All call sites now go through I18n for future translation
- Tests unaffected (still green)
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- getStrings().copied now returns template key "Copied %1 to clipboard" (interpolation at QML call site via .arg)
- Removed unused clearExitNode/setExitNode from getStrings (were dead code)
- Tests updated for new shape while preserving coverage
- New tailscalectl/i18n/ with README + en.json for future-proofing (per dms-plugin-dev best practice)
- Exact behavior preserved; no logic changes
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- lib.js, test/lib.test.js, and all executable JS in TailscaleWidget.qml (onExited handlers, custom methods, onClicked blocks) now terminate every statement with ;
- All if/for/while blocks use {} even for single statements (per repo code style rules)
- No behavior change; all tests continue to pass
- This was the missing follow-up to the previous refactor (the changes existed in the working tree but were never committed on the feature branch)
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- Add PendingAction constant and commandForPendingAction pure function (fully tested)
- Remove root _pendingToggle property
- Use dynamic _pendingAction on the statusCheck Process for the fresh-status hand-off
- Introduce refreshStatus() helper and deduplicate the two post-action refresh sites
- All JS statements now terminate with semicolons; all blocks use braces
- Behavior unchanged; coordination token no longer leaks to widget root
- Enables future post-status actions without adding more root properties
Written by AI agent working for @jtmorris. Model: grok-build-0.1.
- Remove all historical references (#48, first-principles, newly extracted, etc.)
- Remove mentions of deleted components (5s Timer, old copy state machine)
- Make the comment strictly about what the tests actually cover and the real shortcoming (no automated QML coverage)
- Per request: code comments exist to explain the code, not preserve history
- lib.js: single clipboardTools array + getClipboardCommands(text) returning argv lists
- Delete validateClipboardTool, buildCopyCommand, nextClipboardTool, allClipboardTools, safeClipboardTools, old clipboardTools object
- Remove noClipboardTool / invalidClipboardCommand from getStrings (dead)
- TailscaleWidget.qml: remove _copyCurrentTool + _executeCopy; add _copyIndex + _runNextCopy
- copyProcess.onExited and copyToClipboard now purely index-driven, no tool names
- Update tests: remove 4 old clipboard test blocks + dead string assertions; add focused getClipboardCommands tests
- All 34 tests pass
This is the minimal honest design given Process is a QML type.
- Existing toasts + truncated stderr provide the actionable information for users and errors
- Adding persistent debug logs would introduce state, rotation, and maintenance concerns
- with no demonstrable improvement to UX, decisions, or security for normal operation
- Explicit no-op commit closes the planning issue
Written by AI agent working for @jtmorris. Model: Grok build-0.1.
- toggleTailscale() now forces a fresh statusCheck instead of reading possibly-stale root.isConnected
- The actual up/down decision happens in statusCheck.onExited using the just-received value
- _pendingToggle is a transient one-shot flag (reset immediately after use) — not a persistent 'desired state' cache
- Complies with AGENTS.md rule 1 / #11
Written by AI agent working for @jtmorris. Model: Grok build-0.1.
- The new helpers from #43 are now under test
- Added explicit comment explaining that full lifecycle + timing + QML behavior remains manual verification only (no QML test runner exists)
Written by AI agent working for @jtmorris. Model: Grok build-0.1.
- Two small pure UI predicates moved out of QML delegate
- QML now calls TailscaleLib.* (thinner glue)
- New tests + all existing tests pass
- No new state, no architecture change
Written by AI agent working for @jtmorris. Model: Grok build-0.1.
- safeClipboardTools now only dms + wl-copy (clipmanctl removed as niche optional history manager)
- buildCopyCommand now returns pure argv arrays (no sh -c, no escaping, eliminates future injection risk)
- Added getStrings() for centralised user-facing strings (prep for real i18n)
- Updated all tests (TDD) and README
- All 29 tests pass
Written by AI agent working for @jtmorris. Model: Grok build-0.1.
Toast moved out of copyToClipboard() into onExited (code===0 path).
Fallback clipboard rotation also present.
Issue #17 already addressed by existing implementation.
Fixes#17
parseStatusResult safely catches JSON errors (lib.js:68).
onStreamFinished performs no toasts.
All failure toasts centralized in statusCheck.onExited (TailscaleWidget.qml:103).
Eliminates historical double-toast spam described in #13.
Fixes#13
- Revert binaryAvailable property: reintroduced stale-state anti-pattern
already rejected in #11. All error paths already handled by Process
onExited handlers.
- Move statusCheck parsing from onStreamFinished to onExited (#38):
parser now only runs after exit code validation, preventing stale
data from being applied on failure.
- Add refreshTimer guard (#44): timer no longer spawns new statusCheck
while one is already running.
Closes#38, #44. Reverts #52 partial fix.
Toast moved out of copyToClipboard() into onExited (code===0 path).
Fallback clipboard rotation also present.
Issue #17 already addressed by existing implementation.
Fixes#17
Store _pendingToggleAction before starting toggleProcess.
Use it in onExited instead of live root.isConnected.
Prevents 'Failed to disconnect' when user actually clicked connect.
Fixes#14
parseStatusResult safely catches JSON errors (lib.js:68).
onStreamFinished performs no toasts.
All failure toasts centralized in statusCheck.onExited (TailscaleWidget.qml:103).
Eliminates historical double-toast spam described in #13.
Fixes#13
Multiple LLMs keep trying to revert issue #11 unless specifically argued against, and keep sneaking it into code. Other, similar examples of this pathological insistence on storing/caching state that offers no tangible benefit warranting the complexity and edge cases have been tried, but headed off by me earlier.