@aiwg/cli 2026.7.23 → 2026.7.25

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.
@@ -576,7 +576,7 @@ function printSessionReloadNotice(provider) {
576
576
  *
577
577
  * @implements #609
578
578
  */
579
- async function countDeployedArtifacts(target, paths) {
579
+ async function countDeployedArtifacts(target, paths, provider) {
580
580
  const countMd = async (dir) => {
581
581
  if (!dir)
582
582
  return 0;
@@ -633,13 +633,11 @@ async function countDeployedArtifacts(target, paths) {
633
633
  }
634
634
  };
635
635
  // Kernel skills deploy to the platform-native skills dir (always-loaded
636
- // set) while standard skills sequester under <provider>/.aiwg/skills (the
637
- // index-driven discovery tier). Both contribute to the deployed surface,
638
- // so both must be counted (#1228). Derive the kernel path by stripping
639
- // the `.aiwg/` segment from the standard path.
640
- const kernelSkillsPath = paths.skills
641
- ? paths.skills.replace(/(^|\/)\.aiwg\/skills?$/, '$1skills')
642
- : '';
636
+ // set) while standard skills may sequester under <provider>/.aiwg/skills.
637
+ // Count the provider-declared kernel path directly; deriving it by stripping
638
+ // `.aiwg/` from the standard path produced `.codex/skills` instead of
639
+ // Codex's native `.agents/skills` path (#766).
640
+ const kernelSkillsPath = provider ? getProviderKernelSkillsPath(provider) : '';
643
641
  return {
644
642
  agents: await countMd(paths.agents),
645
643
  commands: await countMd(paths.commands),
@@ -785,6 +783,117 @@ async function countBundleSourceArtifacts(bundlePath) {
785
783
  rules: await countMd('rules'),
786
784
  };
787
785
  }
786
+ async function fileExists(filePath) {
787
+ try {
788
+ await fs.access(filePath);
789
+ return true;
790
+ }
791
+ catch {
792
+ return false;
793
+ }
794
+ }
795
+ function resolveDeployPath(target, deployPath) {
796
+ return path.isAbsolute(deployPath) ? deployPath : path.join(target, deployPath);
797
+ }
798
+ async function listBundleMdStems(bundlePath, subdir) {
799
+ try {
800
+ const entries = await fs.readdir(path.join(bundlePath, subdir));
801
+ return entries
802
+ .filter(entry => entry.endsWith('.md'))
803
+ .map(entry => path.basename(entry, '.md'));
804
+ }
805
+ catch {
806
+ return [];
807
+ }
808
+ }
809
+ async function countDeployedBundleFiles(bundlePath, subdir, target, deployPath, extensions) {
810
+ if (!deployPath)
811
+ return 0;
812
+ const stems = await listBundleMdStems(bundlePath, subdir);
813
+ if (stems.length === 0)
814
+ return 0;
815
+ const destDir = resolveDeployPath(target, deployPath);
816
+ let count = 0;
817
+ for (const stem of stems) {
818
+ for (const ext of extensions) {
819
+ if (await fileExists(path.join(destDir, `${stem}${ext}`))) {
820
+ count++;
821
+ break;
822
+ }
823
+ }
824
+ }
825
+ return count;
826
+ }
827
+ async function listBundleSkillNameCandidates(bundlePath) {
828
+ const skillsRoot = path.join(bundlePath, 'skills');
829
+ try {
830
+ const entries = await fs.readdir(skillsRoot, { withFileTypes: true });
831
+ const candidates = [];
832
+ for (const entry of entries) {
833
+ if (!entry.isDirectory())
834
+ continue;
835
+ const sourceName = entry.name;
836
+ const skillMd = path.join(skillsRoot, sourceName, 'SKILL.md');
837
+ let deployedName = sourceName;
838
+ try {
839
+ const content = await fs.readFile(skillMd, 'utf-8');
840
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
841
+ if (match) {
842
+ const parsed = YAML.parse(match[1]);
843
+ if (typeof parsed?.name === 'string' && parsed.name.trim()) {
844
+ deployedName = parsed.name.trim();
845
+ }
846
+ }
847
+ }
848
+ catch {
849
+ // Missing or invalid frontmatter still leaves the source dir name as
850
+ // the best deployed-name approximation for providers that copy dirs.
851
+ }
852
+ candidates.push([...new Set([deployedName, sourceName])]);
853
+ }
854
+ return candidates;
855
+ }
856
+ catch {
857
+ return [];
858
+ }
859
+ }
860
+ async function countDeployedBundleSkills(bundlePath, target, provider, paths) {
861
+ const skillCandidates = await listBundleSkillNameCandidates(bundlePath);
862
+ if (skillCandidates.length === 0)
863
+ return 0;
864
+ const candidateDirs = [
865
+ paths.skills,
866
+ getProviderKernelSkillsPath(provider),
867
+ ]
868
+ .filter(Boolean)
869
+ .map(dir => resolveDeployPath(target, dir));
870
+ const uniqueCandidateDirs = [...new Set(candidateDirs)];
871
+ let count = 0;
872
+ for (const names of skillCandidates) {
873
+ let found = false;
874
+ for (const dir of uniqueCandidateDirs) {
875
+ for (const name of names) {
876
+ if (!(await fileExists(path.join(dir, name, 'SKILL.md'))))
877
+ continue;
878
+ count++;
879
+ found = true;
880
+ break;
881
+ }
882
+ if (found)
883
+ break;
884
+ }
885
+ }
886
+ return count;
887
+ }
888
+ async function countBundleDeployedArtifacts(bundlePath, target, provider) {
889
+ const paths = getProviderPaths(provider);
890
+ return {
891
+ agents: await countDeployedBundleFiles(bundlePath, 'agents', target, paths.agents, ['.md', '.toml']),
892
+ commands: await countDeployedBundleFiles(bundlePath, 'commands', target, paths.commands, ['.md']),
893
+ skills: await countDeployedBundleSkills(bundlePath, target, provider, paths),
894
+ rules: await countDeployedBundleFiles(bundlePath, 'rules', target, paths.rules, ['.md', '.mdc']),
895
+ };
896
+ }
788
897
  /**
789
898
  * Deploy a single project-local bundle to one provider via deploy-agents.mjs.
790
899
  * Runs the same script and flags used for upstream addons, with the bundle
@@ -794,8 +903,8 @@ async function countBundleSourceArtifacts(bundlePath) {
794
903
  */
795
904
  async function deployOneProjectLocalBundle(opts) {
796
905
  const { bundle, ctx, frameworkRoot, provider, target, dryRun, verbose, quiet, modelArgs } = opts;
797
- const counts = await countBundleSourceArtifacts(bundle.artifactPath);
798
- const artifactTotal = counts.agents + counts.commands + counts.skills + counts.rules;
906
+ const sourceCounts = await countBundleSourceArtifacts(bundle.artifactPath);
907
+ const artifactTotal = sourceCounts.agents + sourceCounts.commands + sourceCounts.skills + sourceCounts.rules;
799
908
  let cliCommandCount = 0;
800
909
  try {
801
910
  const contribution = await loadCliCommandsContribution(bundle.artifactPath);
@@ -803,14 +912,14 @@ async function deployOneProjectLocalBundle(opts) {
803
912
  }
804
913
  catch (error) {
805
914
  ui.warn(`Invalid CLI contribution for project-local '${bundle.id}': ${error.message}`);
806
- return { exitCode: 1, counts };
915
+ return { exitCode: 1, counts: sourceCounts };
807
916
  }
808
917
  if (verbose || dryRun) {
809
- ui.dim(` Artifacts: agents=${counts.agents} commands=${counts.commands} skills=${counts.skills} rules=${counts.rules} cli=${cliCommandCount}`);
918
+ ui.dim(` Artifacts: agents=${sourceCounts.agents} commands=${sourceCounts.commands} skills=${sourceCounts.skills} rules=${sourceCounts.rules} cli=${cliCommandCount}`);
810
919
  }
811
920
  if (artifactTotal === 0 && cliCommandCount === 0) {
812
921
  ui.warn(`Project-local ${bundle.type} '${bundle.id}' has no deployable agents, commands, skills, rules, or CLI commands at ${bundle.artifactPath}`);
813
- return { exitCode: 1, counts };
922
+ return { exitCode: 1, counts: sourceCounts };
814
923
  }
815
924
  let exitCode = 0;
816
925
  if (artifactTotal > 0) {
@@ -868,8 +977,9 @@ async function deployOneProjectLocalBundle(opts) {
868
977
  exitCode = 1;
869
978
  }
870
979
  }
871
- // Approximate counts from the bundle's source dirs (deploy-agents.mjs is
872
- // idempotent and copies file-for-file from these dirs)
980
+ const counts = exitCode === 0 && !dryRun
981
+ ? await countBundleDeployedArtifacts(bundle.artifactPath, target, provider)
982
+ : sourceCounts;
873
983
  void ctx;
874
984
  return { exitCode, counts };
875
985
  }
@@ -1865,7 +1975,7 @@ export class UseHandler {
1865
1975
  provider: providerName,
1866
1976
  cwd: target,
1867
1977
  });
1868
- const counts = await countDeployedArtifacts(target, paths);
1978
+ const counts = await countDeployedArtifacts(target, paths, providerName);
1869
1979
  if (quiet) {
1870
1980
  ui.blank();
1871
1981
  if (counts.agents > 0)
@@ -2548,7 +2658,7 @@ export class UseHandler {
2548
2658
  if (quiet) {
2549
2659
  // Count deployed artifacts
2550
2660
  const paths = getProviderPaths(provider);
2551
- counts = await countDeployedArtifacts(target, paths);
2661
+ counts = await countDeployedArtifacts(target, paths, provider);
2552
2662
  if (counts.agents > 0)
2553
2663
  ui.deployCount('Agents', counts.agents);
2554
2664
  if (counts.commands > 0)
@@ -12,7 +12,7 @@ import { readFile, writeFile, mkdir, access, readdir, rename, unlink } from 'fs/
12
12
  import { createHash, randomBytes } from 'crypto';
13
13
  import { resolve, join, isAbsolute } from 'path';
14
14
  import { normalizeNamedCaptures } from '../artifacts/index-builder.js';
15
- import { getProviderDefinition, PROVIDER_IDS, resolveProviderPathValue, } from '../providers/provider-definitions.js';
15
+ import { getProviderDefinition, getProviderKernelSkillPath, PROVIDER_IDS, resolveProviderPathValue, } from '../providers/provider-definitions.js';
16
16
  import { validateAuthorization, } from '../policy/authorization.js';
17
17
  import { projectAiwgPath, resolveProjectAiwgDir } from './project-artifacts.js';
18
18
  import { defaultThreatAssessmentConfig, validateThreatAssessmentConfig, } from '../security/threat-assessment-config.js';
@@ -835,6 +835,7 @@ function getProviderDeployDirs(provider, projectDir) {
835
835
  return {
836
836
  agents: resolveProviderPathValue(artifacts.agents, projectDir),
837
837
  skills: resolveProviderPathValue(artifacts.skills, projectDir),
838
+ kernelSkills: resolveProviderPathValue(getProviderKernelSkillPath(provider), projectDir),
838
839
  commands: resolveProviderPathValue(artifacts.commands, projectDir),
839
840
  rules: resolveProviderPathValue(artifacts.rules, projectDir),
840
841
  };
@@ -877,7 +878,10 @@ export async function populateDeployedTo(config, projectDir) {
877
878
  const counts = {
878
879
  agents: await countDeployedInDir(projectDir, dirs.agents, 'md'),
879
880
  commands: await countDeployedInDir(projectDir, dirs.commands, 'md'),
880
- skills: await countDeployedInDir(projectDir, dirs.skills, 'dirs'),
881
+ skills: (await countDeployedInDir(projectDir, dirs.skills, 'dirs')) +
882
+ (dirs.kernelSkills && dirs.kernelSkills !== dirs.skills
883
+ ? await countDeployedInDir(projectDir, dirs.kernelSkills, 'dirs')
884
+ : 0),
881
885
  rules: await countDeployedInDir(projectDir, dirs.rules, 'md'),
882
886
  };
883
887
  // Only populate if at least one artifact type is present
@@ -593,6 +593,102 @@ export const setupCommand = {
593
593
  },
594
594
  },
595
595
  };
596
+ export const setupGenerateCommand = {
597
+ id: 'setup-generate',
598
+ type: 'skill',
599
+ name: 'Setup Generate',
600
+ description: 'Generate starter setup.aiwg.io/v1 SetupManifest assets for agentic installer automation',
601
+ version: '1.0.0',
602
+ capabilities: ['cli', 'project', 'setup', 'setup-manifest', 'agentic-installer', 'generation', 'automation'],
603
+ keywords: ['setup-generate', 'setup generate', 'SetupManifest', 'setup.aiwg.io/v1', 'agentic-installer', 'manifest generation', 'starter manifest'],
604
+ category: 'project',
605
+ platforms: {
606
+ claude: 'full',
607
+ generic: 'full',
608
+ },
609
+ deployment: {
610
+ pathTemplate: '.{platform}/commands/{id}.md',
611
+ core: true,
612
+ },
613
+ metadata: {
614
+ type: 'skill',
615
+ triggerPhrases: [
616
+ 'generate setup manifest',
617
+ 'create setup.manifest.yaml',
618
+ 'setup-generate',
619
+ 'scaffold installer manifest',
620
+ ],
621
+ commandHint: {
622
+ template: 'utility',
623
+ argumentHint: '[--output <path>] [--name <name>] [--type user|developer|ci] [--platform <os>] [--force] [--json]',
624
+ allowedTools: ['Read', 'Write', 'Bash'],
625
+ },
626
+ },
627
+ };
628
+ export const setupValidateCommand = {
629
+ id: 'setup-validate',
630
+ type: 'skill',
631
+ name: 'Setup Validate',
632
+ description: 'Validate setup.aiwg.io/v1 SetupManifest files against the canonical schema and installer consistency checks',
633
+ version: '1.0.0',
634
+ capabilities: ['cli', 'project', 'setup', 'setup-manifest', 'agentic-installer', 'validation', 'schema'],
635
+ keywords: ['setup-validate', 'setup validate', 'SetupManifest', 'setup.aiwg.io/v1', 'agentic-installer', 'manifest validation', 'schema validation', 'installer consistency'],
636
+ category: 'project',
637
+ platforms: {
638
+ claude: 'full',
639
+ generic: 'full',
640
+ },
641
+ deployment: {
642
+ pathTemplate: '.{platform}/commands/{id}.md',
643
+ core: true,
644
+ },
645
+ metadata: {
646
+ type: 'skill',
647
+ triggerPhrases: [
648
+ 'validate setup manifest',
649
+ 'check setup.manifest.yaml',
650
+ 'setup-validate',
651
+ 'lint installer manifest',
652
+ ],
653
+ commandHint: {
654
+ template: 'utility',
655
+ argumentHint: '[manifest-path] [--manifest <path>] [--strict] [--fix] [--json]',
656
+ allowedTools: ['Read', 'Bash'],
657
+ },
658
+ },
659
+ };
660
+ export const setupRunCommand = {
661
+ id: 'setup-run',
662
+ type: 'skill',
663
+ name: 'Setup Run',
664
+ description: 'Validate and execute setup.aiwg.io/v1 SetupManifest files with installer safety gates',
665
+ version: '1.0.0',
666
+ capabilities: ['cli', 'project', 'setup', 'setup-manifest', 'agentic-installer', 'installer', 'dry-run'],
667
+ keywords: ['setup-run', 'setup run', 'SetupManifest', 'setup.aiwg.io/v1', 'agentic-installer', 'installer runner', 'dry run', 'recovery', 'params'],
668
+ category: 'project',
669
+ platforms: {
670
+ claude: 'full',
671
+ generic: 'full',
672
+ },
673
+ deployment: {
674
+ pathTemplate: '.{platform}/commands/{id}.md',
675
+ core: true,
676
+ },
677
+ metadata: {
678
+ type: 'skill',
679
+ triggerPhrases: [
680
+ 'run setup manifest',
681
+ 'execute installer manifest',
682
+ 'setup-run',
683
+ 'run dev setup',
684
+ ],
685
+ commandHint: {
686
+ template: 'utility',
687
+ argumentHint: '[manifest-path] [--manifest <path>] [--dry-run] [--platform <os>] [--params-file <path>] [--param KEY=VALUE] [--step <id>] [--skip <ids>] [--yes]',
688
+ allowedTools: ['Read', 'Bash'],
689
+ },
690
+ },
691
+ };
596
692
  export const issueCommand = {
597
693
  id: 'issue',
598
694
  type: 'skill',
@@ -3362,6 +3458,9 @@ export const commandDefinitions = [
3362
3458
  newCommand,
3363
3459
  initCommand,
3364
3460
  setupCommand,
3461
+ setupGenerateCommand,
3462
+ setupValidateCommand,
3463
+ setupRunCommand,
3365
3464
  issueCommand,
3366
3465
  issueAuditCommand,
3367
3466
  addressIssuesCommand,
@@ -241,6 +241,35 @@ export function normalizeSandboxEvent(raw) {
241
241
  * Matches the sandbox's 5 s retry interval — suppressess flicker on rapid restarts.
242
242
  */
243
243
  const DEBOUNCE_MS = 5_000;
244
+ function safeTrustRef(value) {
245
+ const ref = typeof value === 'string' ? value.trim() : '';
246
+ if (!ref)
247
+ return undefined;
248
+ if (/-----BEGIN|PRIVATE KEY|TOKEN|SECRET|PASSWORD|[\r\n]/i.test(ref))
249
+ return '[redacted]';
250
+ return ref.slice(0, 160);
251
+ }
252
+ function sanitizeTrustPosture(posture) {
253
+ if (!posture || typeof posture !== 'object')
254
+ return undefined;
255
+ const status = ['secure', 'degraded', 'disabled', 'unknown'].includes(String(posture.status))
256
+ ? posture.status
257
+ : 'unknown';
258
+ return {
259
+ status,
260
+ mode: safeTrustRef(posture.mode),
261
+ ca_provider_ref: safeTrustRef(posture.ca_provider_ref),
262
+ trust_bundle_ref: safeTrustRef(posture.trust_bundle_ref),
263
+ client_identity_ref: safeTrustRef(posture.client_identity_ref),
264
+ rotation_state: safeTrustRef(posture.rotation_state),
265
+ expires_at: safeTrustRef(posture.expires_at),
266
+ trust_bundle_fresh: posture.trust_bundle_fresh,
267
+ missing_required_material: Array.isArray(posture.missing_required_material)
268
+ ? posture.missing_required_material.map((item) => safeTrustRef(item)).filter((item) => Boolean(item))
269
+ : undefined,
270
+ recovery: safeTrustRef(posture.recovery),
271
+ };
272
+ }
244
273
  export class SandboxRegistry {
245
274
  sandboxes = new Map();
246
275
  hitlRequests = new Map();
@@ -336,6 +365,9 @@ export class SandboxRegistry {
336
365
  if (req.ws_capabilities) {
337
366
  existing.wsCapabilities = req.ws_capabilities;
338
367
  }
368
+ if (req.trust_posture) {
369
+ existing.trustPosture = sanitizeTrustPosture(req.trust_posture);
370
+ }
339
371
  this.lastRegistrationTime.set(instanceId, now);
340
372
  return { sandbox_id: existingId, token: existing.token };
341
373
  }
@@ -370,6 +402,7 @@ export class SandboxRegistry {
370
402
  agents: new Map(),
371
403
  sandboxInventory,
372
404
  wsCapabilities: req.ws_capabilities,
405
+ trustPosture: sanitizeTrustPosture(req.trust_posture),
373
406
  };
374
407
  this.sandboxes.set(id, registration);
375
408
  if (instanceId) {
@@ -825,6 +858,7 @@ function toSummary(s) {
825
858
  agents: [...s.agents.values()],
826
859
  sandboxInventory: s.sandboxInventory,
827
860
  wsCapabilities: s.wsCapabilities,
861
+ trustPosture: s.trustPosture,
828
862
  };
829
863
  }
830
864
  // Singleton instance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.7.23",
3
+ "version": "2026.7.25",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",