zigrix 0.1.0-alpha.1 → 0.1.0-alpha.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/dist/doctor.js CHANGED
@@ -19,6 +19,24 @@ export function gatherDoctor(loaded, paths) {
19
19
  const ruleFiles = fs.existsSync(rulesDir)
20
20
  ? fs.readdirSync(rulesDir).filter((f) => f.endsWith('.md')).sort()
21
21
  : [];
22
+ // Non-login shell PATH reachability check
23
+ const nonLoginShellPaths = ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
24
+ const zigrixInNonLoginPath = nonLoginShellPaths.some((dir) => {
25
+ try {
26
+ return fs.existsSync(path.join(dir, 'zigrix'));
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ });
32
+ const zigrixNonLoginLocation = nonLoginShellPaths.find((dir) => {
33
+ try {
34
+ return fs.existsSync(path.join(dir, 'zigrix'));
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }) ?? null;
22
40
  const payload = {
23
41
  node: {
24
42
  executable: process.execPath,
@@ -61,6 +79,11 @@ export function gatherDoctor(loaded, paths) {
61
79
  skillsDir: openclawSkillsDir,
62
80
  skillsDirExists: fs.existsSync(openclawSkillsDir),
63
81
  },
82
+ pathReach: {
83
+ nonLoginShellPaths,
84
+ zigrixInNonLoginPath,
85
+ zigrixNonLoginLocation,
86
+ },
64
87
  };
65
88
  if (!payload.node.ok)
66
89
  warnings.push('Node.js 22+ is required.');
@@ -74,6 +97,10 @@ export function gatherDoctor(loaded, paths) {
74
97
  warnings.push('~/.openclaw not found. OpenClaw integration remains optional.');
75
98
  if (payload.rules.count === 0)
76
99
  warnings.push('No rule files found in rules directory. Seed from orchestration/rules/.');
100
+ if (!payload.pathReach.zigrixInNonLoginPath) {
101
+ warnings.push('zigrix is not reachable from non-login shell PATH (checked: /usr/local/bin, /usr/bin). ' +
102
+ 'Run `zigrix onboard` or manually: sudo ln -sfn $(which zigrix) /usr/local/bin/zigrix');
103
+ }
77
104
  return {
78
105
  ...payload,
79
106
  summary: {
@@ -90,6 +117,7 @@ export function renderDoctorText(payload) {
90
117
  `- Config: ${payload.paths.configPath ?? 'missing'}`,
91
118
  `- Rules dir: ${payload.rules.dir} (${payload.rules.count} files)`,
92
119
  `- OpenClaw home: ${payload.openclaw.home} (${payload.openclaw.exists ? 'present' : 'missing'})`,
120
+ `- Non-login PATH reach: ${payload.pathReach.zigrixInNonLoginPath ? `yes (${payload.pathReach.zigrixNonLoginLocation})` : 'no'}`,
93
121
  `- Ready: ${payload.summary.ready ? 'yes' : 'no'}`,
94
122
  ];
95
123
  for (const warning of payload.summary.warnings) {
package/dist/onboard.d.ts CHANGED
@@ -75,6 +75,12 @@ export declare function checkZigrixInPath(): boolean;
75
75
  * Works whether installed via npm link, npm install -g, or local checkout.
76
76
  */
77
77
  export declare function resolveZigrixBin(): string | null;
78
+ /**
79
+ * Resolve a system-level bin directory that is writable and accessible
80
+ * from non-login shells (e.g., /usr/local/bin).
81
+ * Returns the first writable candidate, or null if none are writable.
82
+ */
83
+ export declare function findSystemBinDir(): string | null;
78
84
  /**
79
85
  * Preferred user-local bin directory for symlink placement.
80
86
  * Returns the first writable directory from a priority list,
@@ -83,9 +89,15 @@ export declare function resolveZigrixBin(): string | null;
83
89
  export declare function findUserBinDir(): string;
84
90
  /**
85
91
  * Ensure zigrix is reachable from PATH.
86
- * If not found, creates a wrapper script in ~/.local/bin/.
92
+ * Priority:
93
+ * 1. /usr/local/bin — stable path, accessible from non-login shells (OpenClaw agents, exec)
94
+ * 2. ~/.local/bin — user-local fallback; may not be in PATH, shows warning if so
95
+ *
96
+ * @param opts._overrideSystemBinDir - Override system bin dir selection (for testing)
87
97
  */
88
- export declare function ensureZigrixInPath(): PathStabilizeResult;
98
+ export declare function ensureZigrixInPath(opts?: {
99
+ _overrideSystemBinDir?: string | null;
100
+ }): PathStabilizeResult;
89
101
  /**
90
102
  * Find the skills/ directory bundled with this zigrix package.
91
103
  */
package/dist/onboard.js CHANGED
@@ -161,6 +161,26 @@ export function resolveZigrixBin() {
161
161
  }
162
162
  return null;
163
163
  }
164
+ /**
165
+ * Resolve a system-level bin directory that is writable and accessible
166
+ * from non-login shells (e.g., /usr/local/bin).
167
+ * Returns the first writable candidate, or null if none are writable.
168
+ */
169
+ export function findSystemBinDir() {
170
+ const candidates = ['/usr/local/bin', '/usr/bin'];
171
+ for (const dir of candidates) {
172
+ try {
173
+ if (fs.existsSync(dir)) {
174
+ fs.accessSync(dir, fs.constants.W_OK);
175
+ return dir;
176
+ }
177
+ }
178
+ catch {
179
+ // not writable or inaccessible
180
+ }
181
+ }
182
+ return null;
183
+ }
164
184
  /**
165
185
  * Preferred user-local bin directory for symlink placement.
166
186
  * Returns the first writable directory from a priority list,
@@ -190,9 +210,13 @@ export function findUserBinDir() {
190
210
  }
191
211
  /**
192
212
  * Ensure zigrix is reachable from PATH.
193
- * If not found, creates a wrapper script in ~/.local/bin/.
213
+ * Priority:
214
+ * 1. /usr/local/bin — stable path, accessible from non-login shells (OpenClaw agents, exec)
215
+ * 2. ~/.local/bin — user-local fallback; may not be in PATH, shows warning if so
216
+ *
217
+ * @param opts._overrideSystemBinDir - Override system bin dir selection (for testing)
194
218
  */
195
- export function ensureZigrixInPath() {
219
+ export function ensureZigrixInPath(opts) {
196
220
  if (checkZigrixInPath()) {
197
221
  return { alreadyInPath: true, symlinkCreated: false, symlinkPath: null, warning: null };
198
222
  }
@@ -205,6 +229,31 @@ export function ensureZigrixInPath() {
205
229
  warning: 'Could not locate zigrix entry point. Ensure zigrix is installed via npm.',
206
230
  };
207
231
  }
232
+ // ── Strategy 1: system bin dir (accessible from non-login shells) ──────────
233
+ const systemBinDir = opts !== undefined && '_overrideSystemBinDir' in opts
234
+ ? opts._overrideSystemBinDir
235
+ : findSystemBinDir();
236
+ if (systemBinDir) {
237
+ const symlinkPath = path.join(systemBinDir, 'zigrix');
238
+ try {
239
+ // Remove stale entry if present
240
+ try {
241
+ if (fs.existsSync(symlinkPath) || fs.lstatSync(symlinkPath).isSymbolicLink()) {
242
+ fs.unlinkSync(symlinkPath);
243
+ }
244
+ }
245
+ catch {
246
+ // doesn't exist — fine
247
+ }
248
+ fs.symlinkSync(binEntry, symlinkPath);
249
+ fs.chmodSync(symlinkPath, 0o755);
250
+ return { alreadyInPath: false, symlinkCreated: true, symlinkPath, warning: null };
251
+ }
252
+ catch {
253
+ // Fall through to user bin dir
254
+ }
255
+ }
256
+ // ── Strategy 2: user-local bin dir ────────────────────────────────────────
208
257
  const userBinDir = findUserBinDir();
209
258
  try {
210
259
  fs.mkdirSync(userBinDir, { recursive: true });
@@ -218,8 +267,6 @@ export function ensureZigrixInPath() {
218
267
  };
219
268
  }
220
269
  const symlinkPath = path.join(userBinDir, 'zigrix');
221
- // Create a wrapper script that invokes node with the correct entry
222
- const wrapper = `#!/usr/bin/env node\nimport('${binEntry.replace(/\\/g, '/')}');\n`;
223
270
  try {
224
271
  // Remove existing if present (stale symlink or old wrapper)
225
272
  if (fs.existsSync(symlinkPath) || fs.lstatSync(symlinkPath).isSymbolicLink()) {
@@ -230,7 +277,6 @@ export function ensureZigrixInPath() {
230
277
  // doesn't exist, fine
231
278
  }
232
279
  try {
233
- // Prefer symlink to the actual CLI bin (simpler, no wrapper needed)
234
280
  fs.symlinkSync(binEntry, symlinkPath);
235
281
  fs.chmodSync(symlinkPath, 0o755);
236
282
  }
@@ -251,10 +297,9 @@ export function ensureZigrixInPath() {
251
297
  // Check if userBinDir is actually in PATH
252
298
  const pathEnv = process.env.PATH ?? '';
253
299
  const inPath = pathEnv.split(path.delimiter).some((d) => path.resolve(d) === path.resolve(userBinDir));
254
- let warning = null;
255
- if (!inPath) {
256
- warning = `Created zigrix at ${symlinkPath}, but ${userBinDir} is not in your PATH. Add it:\n export PATH="${userBinDir}:$PATH"`;
257
- }
300
+ const warning = inPath
301
+ ? null
302
+ : `Created zigrix at ${symlinkPath}, but ${userBinDir} is not in your PATH. Add it:\n export PATH="${userBinDir}:$PATH"`;
258
303
  return { alreadyInPath: false, symlinkCreated: true, symlinkPath, warning };
259
304
  }
260
305
  // ─── OpenClaw skill registration ──────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigrix",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "type": "module",
5
5
  "description": "Multi-project parallel task orchestration CLI for agent-assisted development",
6
6
  "license": "Apache-2.0",