impel-cli 0.11.0 → 0.11.1

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
@@ -633,7 +633,8 @@ Explicitly use the configured impel-acme-research-agent for this request and wai
633
633
  ```
634
634
 
635
635
  Claude exposes native custom agents in its `@` typeahead. Current Codex releases
636
- load and display the generated native agents but do not provide an `@agent`
636
+ load the generated standalone definitions directly from `$CODEX_HOME/agents/`
637
+ but do not provide an `@agent`
637
638
  mention target; named spawning remains model-mediated and may also be limited by
638
639
  the active MultiAgentV2 tool schema. Use `/agent` in Codex to inspect spawned
639
640
  agent threads.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Configure Claude Code and Codex CLI to talk to Impel's gateway, authenticated by an Impel Personal Access Token",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agents.js CHANGED
@@ -398,13 +398,13 @@ function readManifest(manifestPath) {
398
398
 
399
399
  function profileIsFresh(profile, tenantId, now, ttlMs) {
400
400
  const manifest = readManifest(path.join(profile.root, "agents", MANAGED_AGENT_DIRECTORY, MANAGED_AGENT_MANIFEST));
401
- if (!manifest || manifest.tenantId !== tenantId) return false;
401
+ if (!manifest || manifest.version !== 2 || manifest.tenantId !== tenantId) return false;
402
402
  const syncedAt = Date.parse(manifest.syncedAt || "");
403
403
  if (!Number.isFinite(syncedAt) || now - syncedAt >= ttlMs) return false;
404
404
  return manifest.files.every((fileName) =>
405
405
  typeof fileName === "string"
406
406
  && path.basename(fileName) === fileName
407
- && fs.existsSync(path.join(profile.root, "agents", MANAGED_AGENT_DIRECTORY, fileName))
407
+ && fs.existsSync(path.join(profile.root, "agents", fileName))
408
408
  );
409
409
  }
410
410
 
@@ -420,9 +420,25 @@ export function syncAgentProfile({ client, root, label, tenantId, agents, now =
420
420
  const manifestPath = path.join(managedDir, MANAGED_AGENT_MANIFEST);
421
421
  const prior = readManifest(manifestPath);
422
422
  const rendered = renderManagedAgents(client, tenantId, agents);
423
+ const priorFiles = new Set(prior?.files || []);
424
+ const priorUsesDiscoveryRoot = prior?.version === 2;
423
425
 
426
+ // Native clients discover standalone definitions directly under `agents/`.
427
+ // Preflight every destination before writing so an unmanaged file with the
428
+ // same generated name is never overwritten.
424
429
  for (const agent of rendered) {
425
- atomicPrivateWrite(path.join(managedDir, agent.fileName), agent.contents);
430
+ const destination = path.join(agentsDir, agent.fileName);
431
+ if (fs.existsSync(destination)) {
432
+ if (!priorUsesDiscoveryRoot || !priorFiles.has(agent.fileName)) {
433
+ throw new Error(`refusing to overwrite unmanaged native-agent file ${destination}`);
434
+ }
435
+ if (fs.lstatSync(destination).isSymbolicLink()) {
436
+ throw new Error(`refusing to overwrite symlinked native-agent file ${destination}`);
437
+ }
438
+ }
439
+ }
440
+ for (const agent of rendered) {
441
+ atomicPrivateWrite(path.join(agentsDir, agent.fileName), agent.contents);
426
442
  }
427
443
  const currentFiles = new Set(rendered.map((agent) => agent.fileName));
428
444
  for (const stale of prior?.files || []) {
@@ -432,11 +448,22 @@ export function syncAgentProfile({ client, root, label, tenantId, agents, now =
432
448
  && !currentFiles.has(stale)
433
449
  && (stale.endsWith(".md") || stale.endsWith(".toml"))
434
450
  ) {
435
- fs.rmSync(path.join(managedDir, stale), { force: true });
451
+ fs.rmSync(path.join(priorUsesDiscoveryRoot ? agentsDir : managedDir, stale), { force: true });
452
+ }
453
+ }
454
+ if (!priorUsesDiscoveryRoot) {
455
+ for (const legacy of prior?.files || []) {
456
+ if (
457
+ typeof legacy === "string"
458
+ && path.basename(legacy) === legacy
459
+ && (legacy.endsWith(".md") || legacy.endsWith(".toml"))
460
+ ) {
461
+ fs.rmSync(path.join(managedDir, legacy), { force: true });
462
+ }
436
463
  }
437
464
  }
438
465
  atomicPrivateWrite(manifestPath, `${JSON.stringify({
439
- version: 1,
466
+ version: 2,
440
467
  tenantId,
441
468
  client,
442
469
  syncedAt: new Date(now).toISOString(),