distinguish a dev-tree clide from a packaged install (T-256)

CliInstaller.inspect() gains a devTree state: when `clide` on PATH resolves
to a dev-tree build artifact (native/<plat>/clide, the Makefile's
CLIDE_CLI_BIN output) it's classified separately from a packaged install
rather than silently treated as "installed". needsInstall stays false for a
dev build (it's intentional on a checkout, not a reinstall prompt), and the
launch-time check surfaces it as an info note. install() flags fromDevTree +
notes it in the result message when the copied source is a dev build.

Closes the last open child of the T-208 "give Claude hands" initiative.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 21:53:01 +02:00
co-authored by Claude Opus 4.8
parent 9cb7da3f57
commit 51cfa15c78
7 changed files with 116 additions and 4 deletions
@@ -85,6 +85,22 @@ void main() {
expect(notes.first.title, 'clide CLI is stale');
});
test('notes a dev-tree build on activation (info, not a warning) (T-256)', () async {
final dev = touchExec('${tmp.path}/native/linux-x64/clide').path;
final binDir = Directory('${tmp.path}/bin')..createSync();
Link('${binDir.path}/clide').createSync(dev);
await boot(CliInstaller(
resolvedExecutable: '${tmp.path}/gui/clide',
env: {'PATH': binDir.path},
bundledClientCandidates: const [],
installDir: binDir.path,
));
final notes = f.services.notify.active;
expect(notes, isNotEmpty);
expect(notes.first.level, NotificationLevel.info);
expect(notes.first.title, 'clide CLI: dev build');
});
test('does not warn when clide is already installed', () async {
final binDir = Directory('${tmp.path}/bin')..createSync();
touchExec('${binDir.path}/clide');