@voidlabs.gg/voidshift 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ One CLI for the Void dev tools. Install it once, run `vsf` (short for
5
5
  defaults you can change. Everything setup does can be seen and changed later
6
6
  from the command line.
7
7
 
8
- > Early release. macOS only for now; Windows and Linux are next.
8
+ > Early release. macOS and Linux (awake is macOS only); Windows is next.
9
9
 
10
10
  ```sh
11
11
  npm i -g @voidlabs.gg/voidshift
@@ -104,10 +104,13 @@ to trust the recall hook once: run `/hooks` in Codex.
104
104
  - **Your own logins.** Agents run the official `claude` and `codex` CLIs you
105
105
  already use, signed in with your own account. VoidShift never reads or
106
106
  stores those credentials. The Linear API key for the runner lives in your
107
- macOS Keychain.
107
+ macOS Keychain, or on Linux in `~/.config/voidshift/agents/secrets.json`
108
+ (mode 0600).
108
109
  - **Trust.** The Dev agent can run shell commands inside its copy of the repo
109
110
  so it can install and test. Review its pull requests like any other.
110
111
  - **Your config** lives in `~/.config/voidshift`.
112
+ - **Linux.** The runner is a systemd user timer. Run
113
+ `sudo loginctl enable-linger $USER` so it keeps going after you log out.
111
114
 
112
115
  ```sh
113
116
  vsf doctor # check everything is set up, and whether an update is out
package/dist/checks.js CHANGED
@@ -19,23 +19,24 @@ function firstLine(s) {
19
19
  }
20
20
  export function baseChecks(platform, nodeVersion, exec) {
21
21
  const checks = [];
22
- checks.push(platform === 'darwin'
23
- ? { name: 'macOS', status: 'ok', detail: 'supported' }
24
- : { name: 'macOS', status: 'fail', detail: `${platform} is not supported yet`, fix: 'The MVP runs on macOS only' });
22
+ const linux = platform === 'linux';
23
+ checks.push(platform === 'darwin' || linux
24
+ ? { name: 'platform', status: 'ok', detail: linux ? 'Linux, supported' : 'macOS, supported' }
25
+ : { name: 'platform', status: 'fail', detail: `${platform} is not supported yet`, fix: 'Run on macOS or Linux' });
25
26
  checks.push(atLeast(nodeVersion, '22.18')
26
27
  ? { name: 'Node', status: 'ok', detail: nodeVersion }
27
28
  : { name: 'Node', status: 'fail', detail: `${nodeVersion} is too old`, fix: 'Install Node 22.18 or newer' });
28
29
  const git = exec('git', ['--version']);
29
30
  checks.push(git.ok
30
31
  ? { name: 'git', status: 'ok', detail: git.stdout }
31
- : { name: 'git', status: 'fail', detail: 'not found', fix: 'xcode-select --install' });
32
+ : { name: 'git', status: 'fail', detail: 'not found', fix: linux ? 'Install git with your package manager' : 'xcode-select --install' });
32
33
  const gh = exec('gh', ['auth', 'token']);
33
34
  const ghInstalled = gh.ok || exec('gh', ['--version']).ok;
34
35
  checks.push(gh.ok
35
36
  ? { name: 'GitHub CLI', status: 'ok', detail: 'logged in' }
36
37
  : ghInstalled
37
38
  ? { name: 'GitHub CLI', status: 'warn', detail: 'not logged in', fix: 'gh auth login' }
38
- : { name: 'GitHub CLI', status: 'warn', detail: 'not found', fix: 'brew install gh' });
39
+ : { name: 'GitHub CLI', status: 'warn', detail: 'not found', fix: linux ? 'https://cli.github.com' : 'brew install gh' });
39
40
  const claude = exec('claude', ['--version']);
40
41
  const codex = exec('codex', ['--version']);
41
42
  checks.push(claude.ok
@@ -2,7 +2,7 @@ import * as p from '@clack/prompts';
2
2
  import { baseChecks, run } from "../checks.js";
3
3
  import { context, readConfig, writeConfig } from "../config.js";
4
4
  import { fetchTool, loadTool } from "../loader.js";
5
- import { findTool, registry } from "../registry.js";
5
+ import { findTool, registry, unavailable } from "../registry.js";
6
6
  import { needsTerminal, parseFlags } from "./args.js";
7
7
  import { usages } from "./help.js";
8
8
  import { Cancelled } from "../ui.js";
@@ -82,18 +82,18 @@ export async function setup(defaults = false) {
82
82
  const ctx = context(true, defaults);
83
83
  const config = readConfig();
84
84
  try {
85
- const available = registry.filter((t) => t.status !== 'planned').map((t) => t.name);
85
+ const available = registry.filter((t) => !unavailable(t)).map((t) => t.name);
86
86
  const picked = defaults
87
- ? [...new Set([...config.tools, ...available])]
87
+ ? [...new Set([...config.tools.filter((n) => available.includes(n)), ...available])]
88
88
  : await p.multiselect({
89
89
  message: 'Which tools do you want?',
90
90
  options: registry.map((t) => ({
91
91
  value: t.name,
92
92
  label: t.name,
93
- hint: t.status === 'planned' ? `${t.summary} (coming soon)` : t.summary,
94
- disabled: t.status === 'planned',
93
+ hint: unavailable(t) ? `${t.summary} (${unavailable(t)})` : t.summary,
94
+ disabled: !!unavailable(t),
95
95
  })),
96
- initialValues: config.tools,
96
+ initialValues: config.tools.filter((n) => available.includes(n)),
97
97
  required: false,
98
98
  });
99
99
  if (p.isCancel(picked))
@@ -1,12 +1,13 @@
1
1
  import { context, readConfig, writeConfig } from "../config.js";
2
2
  import { fetchTool, loadTool } from "../loader.js";
3
- import { findTool, registry } from "../registry.js";
3
+ import { findTool, registry, unavailable } from "../registry.js";
4
4
  import { needsTerminal, wantsHelp } from "./args.js";
5
5
  export function listTools() {
6
6
  const installed = new Set(readConfig().tools);
7
7
  const width = Math.max(...registry.map((t) => t.name.length));
8
8
  for (const t of registry) {
9
- const tag = installed.has(t.name) ? ' (added)' : t.status === 'planned' ? ' (coming soon)' : '';
9
+ const why = unavailable(t);
10
+ const tag = installed.has(t.name) ? ' (added)' : why ? ` (${why})` : '';
10
11
  console.log(`${t.name.padEnd(width)} ${t.summary}${tag}`);
11
12
  }
12
13
  return 0;
@@ -31,6 +32,10 @@ export async function addTool(name) {
31
32
  console.error(`${entry.name} is not released yet.`);
32
33
  return 1;
33
34
  }
35
+ if (unavailable(entry)) {
36
+ console.error(`${entry.name} runs on macOS only.`);
37
+ return 1;
38
+ }
34
39
  const ctx = context();
35
40
  if (!fetchTool(entry, ctx.configDir)) {
36
41
  console.error(`could not download ${entry.pkg}`);
@@ -3,6 +3,9 @@ export interface ToolEntry {
3
3
  summary: string;
4
4
  pkg: string;
5
5
  status: 'planned' | 'available';
6
+ platforms?: NodeJS.Platform[];
6
7
  }
7
8
  export declare const registry: ToolEntry[];
9
+ export declare const runsHere: (t: ToolEntry, platform?: NodeJS.Platform) => boolean;
10
+ export declare function unavailable(t: ToolEntry, platform?: NodeJS.Platform): string | null;
8
11
  export declare function findTool(name: string): ToolEntry | undefined;
package/dist/registry.js CHANGED
@@ -7,6 +7,7 @@ export const registry = [
7
7
  summary: 'Keep the Mac running on battery with the lid closed, with guardrails',
8
8
  pkg: '@voidlabs.gg/voidshift-awake',
9
9
  status: 'available',
10
+ platforms: ['darwin'],
10
11
  },
11
12
  {
12
13
  name: 'agents',
@@ -21,6 +22,13 @@ export const registry = [
21
22
  status: 'available',
22
23
  },
23
24
  ];
25
+ export const runsHere = (t, platform = process.platform) => !t.platforms || t.platforms.includes(platform);
26
+ // Why a tool cannot be added here, or null when it can.
27
+ export function unavailable(t, platform = process.platform) {
28
+ if (t.status === 'planned')
29
+ return 'coming soon';
30
+ return runsHere(t, platform) ? null : 'macOS only';
31
+ }
24
32
  export function findTool(name) {
25
33
  return registry.find((t) => t.name === name);
26
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidlabs.gg/voidshift",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "One CLI for the Void dev tools: keep your Mac awake, run agents in the background, shared skills and memory for Claude Code and Codex.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,7 +18,8 @@
18
18
  "agents",
19
19
  "linear",
20
20
  "cli",
21
- "macos"
21
+ "macos",
22
+ "linux"
22
23
  ],
23
24
  "type": "module",
24
25
  "bin": {
@@ -30,9 +31,6 @@
30
31
  "LICENSE",
31
32
  "README.md"
32
33
  ],
33
- "os": [
34
- "darwin"
35
- ],
36
34
  "engines": {
37
35
  "node": ">=22.18"
38
36
  },