agent-sin 0.1.3 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -15,6 +15,34 @@ See the [compatibility policy](https://agent.shingoirie.com/versioning) for deta
15
15
 
16
16
  ---
17
17
 
18
+ ## [0.1.6] — 2026-05-14
19
+
20
+ ### Fixed
21
+
22
+ - On Windows, launching the Codex CLI (and Claude Code CLI) failed with `spawn codex ENOENT` because Node's `spawn` cannot resolve the `.cmd` shim that npm-installed CLIs use on Windows. Both the one-shot CLI bridge and the long-running `codex app-server` now enable `shell: true` only on `win32`, so the shim is found correctly. POSIX paths still use direct exec.
23
+
24
+ ---
25
+
26
+ ## [0.1.5] — 2026-05-14
27
+
28
+ ### Changed
29
+
30
+ - The interactive CLI (`agent-sin start` / `agent-sin chat`) now hits the npm registry on every startup instead of trusting the 24-hour cache, so new releases are surfaced immediately. Background paths (gateway, mid-conversation polls) still use the cache.
31
+
32
+ ---
33
+
34
+ ## [0.1.4] — 2026-05-14
35
+
36
+ ### Added
37
+
38
+ - `agent-sin --version` / `-v` / `agent-sin version` now print the installed version. Previously these printed an "unknown command" error.
39
+
40
+ ### Fixed
41
+
42
+ - Update-notifier now refreshes its cache when the installed version has caught up to or passed the previously cached "latest". Previously, after upgrading to the cached latest, the cache stayed valid for 24 hours and hid the banner for the next release. The CLI also fetches synchronously (with a short timeout) on the very first run so the banner appears immediately when a new version is available.
43
+
44
+ ---
45
+
18
46
  ## [0.1.3] — 2026-05-14
19
47
 
20
48
  ### Added
package/dist/cli/index.js CHANGED
@@ -30,6 +30,10 @@ import { agentSinVersion, agentSinVersionFresh } from "../core/version.js";
30
30
  import { appendHistory, chatRespond, makeSpinnerProgress, } from "../core/chat-engine.js";
31
31
  async function main() {
32
32
  const [command, ...args] = process.argv.slice(2);
33
+ if (command === "--version" || command === "-v" || command === "version") {
34
+ console.log(agentSinVersion());
35
+ return 0;
36
+ }
33
37
  try {
34
38
  const dotenv = await loadDotenv();
35
39
  if (dotenv.permission_warning) {
@@ -369,6 +373,12 @@ async function cmdRun(args) {
369
373
  async function cmdChat(args) {
370
374
  const config = await loadConfig();
371
375
  scheduleUpdateCheck(config.workspace);
376
+ // Interactive CLI: always check the registry on startup so the user sees
377
+ // any new release immediately, not on the next session.
378
+ const startupBanner = await consumeUpdateBanner(config.workspace, { force: true });
379
+ if (startupBanner) {
380
+ console.log(startupBanner);
381
+ }
372
382
  const history = [];
373
383
  if (args.length > 0) {
374
384
  const lines = await handleChatMessage(config, args.join(" "), history);
@@ -643,7 +643,14 @@ function splitExtraArgs(value) {
643
643
  }
644
644
  async function spawnCli(bin, args, modelId, provider, onProgress, cwd) {
645
645
  return new Promise((resolve, reject) => {
646
- const child = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"], cwd });
646
+ // Windows wraps CLI tools as .cmd / .bat shims (codex.cmd, claude.cmd).
647
+ // Node's spawn cannot exec those without going through a shell, so we
648
+ // enable shell only on win32. Posix paths keep direct exec for safety.
649
+ const child = spawn(bin, args, {
650
+ stdio: ["ignore", "pipe", "pipe"],
651
+ cwd,
652
+ shell: process.platform === "win32",
653
+ });
647
654
  const stdout = [];
648
655
  const stderr = [];
649
656
  let stderrLine = "";
@@ -1,2 +1,4 @@
1
1
  export declare function scheduleUpdateCheck(workspace?: string): void;
2
- export declare function consumeUpdateBanner(workspace?: string): Promise<string | null>;
2
+ export declare function consumeUpdateBanner(workspace?: string, options?: {
3
+ force?: boolean;
4
+ }): Promise<string | null>;
@@ -104,14 +104,25 @@ function isDisabled() {
104
104
  const flag = (process.env.AGENT_SIN_DISABLE_UPDATE_CHECK || "").trim().toLowerCase();
105
105
  return flag === "1" || flag === "true" || flag === "yes";
106
106
  }
107
+ function isCacheStale(cache, current) {
108
+ if (!cache.latestVersion)
109
+ return true;
110
+ // The cached "latest" trailing the installed version means we upgraded past
111
+ // the previously seen latest. Re-check immediately rather than waiting 24h.
112
+ if (compareSemver(cache.latestVersion, current) <= 0)
113
+ return true;
114
+ const lastChecked = cache.lastCheckedAt ? Date.parse(cache.lastCheckedAt) : 0;
115
+ if (!Number.isFinite(lastChecked))
116
+ return true;
117
+ return Date.now() - lastChecked >= CHECK_INTERVAL_MS;
118
+ }
107
119
  export function scheduleUpdateCheck(workspace = defaultWorkspace()) {
108
120
  if (isDisabled())
109
121
  return;
110
122
  void (async () => {
111
123
  const cache = await loadCache(workspace);
112
- const now = Date.now();
113
- const lastChecked = cache.lastCheckedAt ? Date.parse(cache.lastCheckedAt) : 0;
114
- if (Number.isFinite(lastChecked) && now - lastChecked < CHECK_INTERVAL_MS)
124
+ const current = await readCurrentVersion();
125
+ if (!isCacheStale(cache, current))
115
126
  return;
116
127
  const latest = await fetchLatestVersion();
117
128
  if (!latest)
@@ -123,13 +134,27 @@ export function scheduleUpdateCheck(workspace = defaultWorkspace()) {
123
134
  });
124
135
  })();
125
136
  }
126
- export async function consumeUpdateBanner(workspace = defaultWorkspace()) {
137
+ export async function consumeUpdateBanner(workspace = defaultWorkspace(), options = {}) {
127
138
  if (isDisabled())
128
139
  return null;
129
- const cache = await loadCache(workspace);
140
+ let cache = await loadCache(workspace);
141
+ const current = await readCurrentVersion();
142
+ // Interactive entry points pass force=true to always fetch the latest
143
+ // version on startup; background paths (services, mid-conversation polls)
144
+ // fall back to the cache to avoid hammering the registry.
145
+ if (options.force || isCacheStale(cache, current)) {
146
+ const latest = await fetchLatestVersion();
147
+ if (latest) {
148
+ cache = {
149
+ ...cache,
150
+ lastCheckedAt: new Date().toISOString(),
151
+ latestVersion: latest,
152
+ };
153
+ await saveCache(workspace, cache);
154
+ }
155
+ }
130
156
  if (!cache.latestVersion)
131
157
  return null;
132
- const current = await readCurrentVersion();
133
158
  if (compareSemver(cache.latestVersion, current) <= 0)
134
159
  return null;
135
160
  const today = todayKey();
@@ -104,8 +104,11 @@ export class CodexAppServerSession {
104
104
  await this.starting;
105
105
  }
106
106
  async start() {
107
+ // Windows wraps the codex CLI as a .cmd shim; Node's spawn cannot exec
108
+ // those without a shell, so we enable shell only on win32.
107
109
  const child = spawn(this.options.bin, this.options.args, {
108
110
  stdio: ["pipe", "pipe", "pipe"],
111
+ shell: process.platform === "win32",
109
112
  });
110
113
  this.child = child;
111
114
  this.exitReason = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-sin",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Program Skill-first personal AI agent OS CLI.",
5
5
  "type": "module",
6
6
  "license": "MIT",