@raishin/vanguard-frontier-agentic 2.5.0 → 2.6.0

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.
Files changed (37) hide show
  1. package/.agents/tasks/task-dynamic-kiro-powers/2025-01-24-120000-review.md +92 -0
  2. package/.agents/tasks/task-dynamic-kiro-powers/context.json +22 -0
  3. package/.agents/tasks/task-dynamic-kiro-powers/features/FEAT-001.json +34 -0
  4. package/.agents/tasks/task-dynamic-kiro-powers/task.json +14 -0
  5. package/.claude-plugin/marketplace.json +1 -1
  6. package/.claude-plugin/plugin.json +1 -1
  7. package/.cursor-plugin/plugin.json +1 -1
  8. package/.github/plugin/marketplace.json +1 -1
  9. package/README.md +2 -0
  10. package/catalog/asset-integrity.json +129 -29
  11. package/package.json +3 -1
  12. package/plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json +3 -2
  13. package/plugins/vanguard-frontier-agentic/skills/vanguard-frontier-agentic-install/SKILL.md +37 -0
  14. package/powers/README.md +28 -10
  15. package/powers/vanguard-argocd/POWER.md +40 -0
  16. package/powers/vanguard-backstage/POWER.md +40 -0
  17. package/powers/vanguard-cert-manager/POWER.md +40 -0
  18. package/powers/vanguard-cilium/POWER.md +40 -0
  19. package/powers/vanguard-dotnet/POWER.md +41 -0
  20. package/powers/vanguard-falco/POWER.md +40 -0
  21. package/powers/vanguard-fluxcd/POWER.md +40 -0
  22. package/powers/vanguard-generic/POWER.md +40 -0
  23. package/powers/vanguard-hr/POWER.md +41 -0
  24. package/powers/vanguard-istio/POWER.md +40 -0
  25. package/powers/vanguard-kyverno/POWER.md +40 -0
  26. package/powers/vanguard-legal/POWER.md +41 -0
  27. package/powers/vanguard-marketing/POWER.md +41 -0
  28. package/powers/vanguard-multi-cloud/POWER.md +41 -0
  29. package/powers/vanguard-opentelemetry/POWER.md +40 -0
  30. package/powers/vanguard-prometheus/POWER.md +40 -0
  31. package/powers/vanguard-sigstore/POWER.md +40 -0
  32. package/scripts/export-marketplace-agents.mjs +26 -0
  33. package/scripts/generate-kiro-powers.mjs +360 -5
  34. package/scripts/install-codex-home.mjs +95 -0
  35. package/tests/test-codex-plugin-marketplace-install.test.mjs +132 -0
  36. package/tests/test-vfa-export-coverage.test.mjs +108 -0
  37. package/tests/validate-codex-marketplace.py +23 -1
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: "vanguard-multi-cloud"
3
+ displayName: "Vanguard Frontier — Multi-Cloud"
4
+ description: "Curated Multi-Cloud agents for ai economist, cloud price advisor. Routes via finops-maestro-agent to specialist agents based on task scope. Static review only; no live mutations."
5
+ keywords: ["finops", "cloud-pricing", "cost-optimization", "reserved-instances"]
6
+ author: "Raishin"
7
+ ---
8
+ # Vanguard Frontier — Multi-Cloud
9
+
10
+ Curated Multi-Cloud agents for ai economist, cloud price advisor. Routes via finops-maestro-agent to specialist agents based on task scope. Static review only; no live mutations.
11
+
12
+ ## When to engage this Power
13
+
14
+ Activate when the task references Multi-Cloud services, resources, or operations. Do not activate on unrelated requests — narrow keyword matching is required to avoid false activations (Kiro Powers convention).
15
+
16
+ ## Routing pattern
17
+
18
+ - **`finops-maestro-agent`** — classifies and routes the task to the right specialist
19
+
20
+ Use the maestro as the entry point: classify the task, then dispatch to one specialist or a parallel team of specialists. Never have the maestro itself execute a live mutation.
21
+
22
+ ## Live-guard agents (gate_mode only)
23
+
24
+ - *(none — this provider has no live-mutation guards in the catalog)*
25
+
26
+ Live-guard agents enforce approval, target confirmation, evidence capture, and rollback plans before executing a mutation. They are never auto-dispatched — the maestro must place them in `live-guard-gate` or `runtime-evidence-gate` mode.
27
+
28
+ ## Invariants
29
+
30
+ - Route all tasks through finops-maestro-agent for proper classification and dispatch.
31
+ - Static review only -- agents analyze configuration and provide findings without mutating live systems.
32
+ - Cost recommendations are estimates based on public pricing; verify against actual billing before acting.
33
+
34
+ ## Where the agents live
35
+
36
+ Agent specs and adapters are part of the [Vanguard Frontier Agentic](https://github.com/Raishin/vanguard-frontier-agentic) marketplace. For this provider, see `agents/multi-cloud/` in that repository. All 3 agents in this provider ship a Kiro adapter (`harnesses/kiro-ide.agent.md`, `kiro-cli.agent.json`).
37
+
38
+ ## Companion install paths
39
+
40
+ - **Claude Code:** `/plugin marketplace add Raishin/vanguard-frontier-agentic` then `/plugin install vanguard-frontier-agentic@vanguard-frontier-agentic`
41
+ - **Codex / Copilot / Cursor / Gemini CLI / Kiro (file export):** `npx vfa-export-agents --platform <harness> --provider multi-cloud --repo .`
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: "vanguard-opentelemetry"
3
+ displayName: "Vanguard Frontier — OpenTelemetry"
4
+ description: "Reviews openTelemetry Collector pipeline configuration — receiver/processor/exporter ordering, memory_limiter placement,... Static review only; no live mutations."
5
+ keywords: ["opentelemetry", "otel-collector", "tracing", "observability-pipeline"]
6
+ author: "Raishin"
7
+ ---
8
+ # Vanguard Frontier — OpenTelemetry
9
+
10
+ Reviews openTelemetry Collector pipeline configuration — receiver/processor/exporter ordering, memory_limiter placement,... Static review only; no live mutations.
11
+
12
+ ## When to engage this Power
13
+
14
+ Activate when the task references OpenTelemetry services, resources, or operations. Do not activate on unrelated requests — narrow keyword matching is required to avoid false activations (Kiro Powers convention).
15
+
16
+ ## Routing pattern
17
+
18
+ - *(no maestro for this provider; reference agents directly under `agents/opentelemetry/`)*
19
+
20
+ Reference agents directly from agents/opentelemetry/ without maestro-based routing.
21
+
22
+ ## Live-guard agents (gate_mode only)
23
+
24
+ - *(none — this provider has no live-mutation guards in the catalog)*
25
+
26
+ Live-guard agents enforce approval, target confirmation, evidence capture, and rollback plans before executing a mutation. They are never auto-dispatched — the maestro must place them in `live-guard-gate` or `runtime-evidence-gate` mode.
27
+
28
+ ## Invariants
29
+
30
+ - Static review only -- agents analyze configuration and provide findings without mutating live systems.
31
+ - Collector pipeline changes affect observability for all instrumented services; review cardinality impact.
32
+
33
+ ## Where the agents live
34
+
35
+ Agent specs and adapters are part of the [Vanguard Frontier Agentic](https://github.com/Raishin/vanguard-frontier-agentic) marketplace. For this provider, see `agents/opentelemetry/` in that repository. The single agent in this provider ships a Kiro adapter (`harnesses/kiro-ide.agent.md`, `kiro-cli.agent.json`).
36
+
37
+ ## Companion install paths
38
+
39
+ - **Claude Code:** `/plugin marketplace add Raishin/vanguard-frontier-agentic` then `/plugin install vanguard-frontier-agentic@vanguard-frontier-agentic`
40
+ - **Codex / Copilot / Cursor / Gemini CLI / Kiro (file export):** `npx vfa-export-agents --platform <harness> --provider opentelemetry --repo .`
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: "vanguard-prometheus"
3
+ displayName: "Vanguard Frontier — Prometheus"
4
+ description: "Reviews prometheus and AlertManager configuration for cardinality risks, alert correctness, scrape security, routing safety,... Static review only; no live mutations."
5
+ keywords: ["prometheus", "alertmanager", "metrics-cardinality", "scrape-config"]
6
+ author: "Raishin"
7
+ ---
8
+ # Vanguard Frontier — Prometheus
9
+
10
+ Reviews prometheus and AlertManager configuration for cardinality risks, alert correctness, scrape security, routing safety,... Static review only; no live mutations.
11
+
12
+ ## When to engage this Power
13
+
14
+ Activate when the task references Prometheus services, resources, or operations. Do not activate on unrelated requests — narrow keyword matching is required to avoid false activations (Kiro Powers convention).
15
+
16
+ ## Routing pattern
17
+
18
+ - *(no maestro for this provider; reference agents directly under `agents/prometheus/`)*
19
+
20
+ Reference agents directly from agents/prometheus/ without maestro-based routing.
21
+
22
+ ## Live-guard agents (gate_mode only)
23
+
24
+ - *(none — this provider has no live-mutation guards in the catalog)*
25
+
26
+ Live-guard agents enforce approval, target confirmation, evidence capture, and rollback plans before executing a mutation. They are never auto-dispatched — the maestro must place them in `live-guard-gate` or `runtime-evidence-gate` mode.
27
+
28
+ ## Invariants
29
+
30
+ - Static review only -- agents analyze configuration and provide findings without mutating live systems.
31
+ - Alerting rule and scrape config changes affect monitoring coverage; review for metric-name collisions.
32
+
33
+ ## Where the agents live
34
+
35
+ Agent specs and adapters are part of the [Vanguard Frontier Agentic](https://github.com/Raishin/vanguard-frontier-agentic) marketplace. For this provider, see `agents/prometheus/` in that repository. The single agent in this provider ships a Kiro adapter (`harnesses/kiro-ide.agent.md`, `kiro-cli.agent.json`).
36
+
37
+ ## Companion install paths
38
+
39
+ - **Claude Code:** `/plugin marketplace add Raishin/vanguard-frontier-agentic` then `/plugin install vanguard-frontier-agentic@vanguard-frontier-agentic`
40
+ - **Codex / Copilot / Cursor / Gemini CLI / Kiro (file export):** `npx vfa-export-agents --platform <harness> --provider prometheus --repo .`
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: "vanguard-sigstore"
3
+ displayName: "Vanguard Frontier — Sigstore"
4
+ description: "Reviews cosign image signing, Kyverno imageVerify policy identity constraints, SBOM and SLSA provenance attestations, Rekor... Static review only; no live mutations."
5
+ keywords: ["sigstore", "cosign", "supply-chain-integrity", "image-signing"]
6
+ author: "Raishin"
7
+ ---
8
+ # Vanguard Frontier — Sigstore
9
+
10
+ Reviews cosign image signing, Kyverno imageVerify policy identity constraints, SBOM and SLSA provenance attestations, Rekor... Static review only; no live mutations.
11
+
12
+ ## When to engage this Power
13
+
14
+ Activate when the task references Sigstore services, resources, or operations. Do not activate on unrelated requests — narrow keyword matching is required to avoid false activations (Kiro Powers convention).
15
+
16
+ ## Routing pattern
17
+
18
+ - *(no maestro for this provider; reference agents directly under `agents/sigstore/`)*
19
+
20
+ Reference agents directly from agents/sigstore/ without maestro-based routing.
21
+
22
+ ## Live-guard agents (gate_mode only)
23
+
24
+ - *(none — this provider has no live-mutation guards in the catalog)*
25
+
26
+ Live-guard agents enforce approval, target confirmation, evidence capture, and rollback plans before executing a mutation. They are never auto-dispatched — the maestro must place them in `live-guard-gate` or `runtime-evidence-gate` mode.
27
+
28
+ ## Invariants
29
+
30
+ - Static review only -- agents analyze configuration and provide findings without mutating live systems.
31
+ - Supply-chain policy changes can block valid deployments; verify cosign keyless trust roots before enforcement.
32
+
33
+ ## Where the agents live
34
+
35
+ Agent specs and adapters are part of the [Vanguard Frontier Agentic](https://github.com/Raishin/vanguard-frontier-agentic) marketplace. For this provider, see `agents/sigstore/` in that repository. The single agent in this provider ships a Kiro adapter (`harnesses/kiro-ide.agent.md`, `kiro-cli.agent.json`).
36
+
37
+ ## Companion install paths
38
+
39
+ - **Claude Code:** `/plugin marketplace add Raishin/vanguard-frontier-agentic` then `/plugin install vanguard-frontier-agentic@vanguard-frontier-agentic`
40
+ - **Codex / Copilot / Cursor / Gemini CLI / Kiro (file export):** `npx vfa-export-agents --platform <harness> --provider sigstore --repo .`
@@ -45,6 +45,7 @@ const PLATFORM_ALIASES = {
45
45
  };
46
46
 
47
47
  const SKILLS_PLATFORM_CONFIG = {
48
+ codex: ".codex/skills",
48
49
  "claude-code": ".claude/skills",
49
50
  copilot: ".github/skills",
50
51
  gemini: ".gemini/skills",
@@ -332,6 +333,14 @@ function copySkillTree(sourceDir, destDir, force) {
332
333
  if (entry.isSymbolicLink()) {
333
334
  throw new Error(`Refusing to copy symbolic link in skill tree: ${src}`);
334
335
  }
336
+ let dstLstat = null;
337
+ try { dstLstat = fs.lstatSync(dst); } catch { /* dst does not exist – fine */ }
338
+ if (dstLstat && dstLstat.isSymbolicLink()) {
339
+ throw new Error(
340
+ `Refusing to write to symbolic link destination in skill tree: ${dst}. ` +
341
+ `Remove the symlink and retry.`
342
+ );
343
+ }
335
344
  if (entry.isDirectory()) {
336
345
  copySkillTree(src, dst, force);
337
346
  continue;
@@ -420,6 +429,20 @@ function copyFile(source, destination, force) {
420
429
  fs.copyFileSync(source, destination);
421
430
  }
422
431
 
432
+ function rewriteCodexAgentSkillPaths(agentFile, targetRoot) {
433
+ const text = fs.readFileSync(agentFile, "utf8");
434
+ const rewritten = text.replace(
435
+ /^path = "skills\/[^"\n]+\/([^/"\n]+)\/SKILL\.md"$/gm,
436
+ (_match, skillName) => {
437
+ const skillDir = path.join(targetRoot, SKILLS_PLATFORM_CONFIG.codex, skillName);
438
+ return `path = ${JSON.stringify(skillDir)}`;
439
+ }
440
+ );
441
+ if (rewritten !== text) {
442
+ fs.writeFileSync(agentFile, rewritten);
443
+ }
444
+ }
445
+
423
446
  function loadRoles() {
424
447
  const rolesPath = path.join(repoRoot, "catalog", "install-roles.json");
425
448
  if (!fs.existsSync(rolesPath)) {
@@ -640,6 +663,9 @@ function main() {
640
663
  for (const operation of operations) {
641
664
  assertWithin(args.repo, operation.dest, "write destination");
642
665
  copyFile(operation.source, operation.dest, args.force);
666
+ if (platform === "codex") {
667
+ rewriteCodexAgentSkillPaths(operation.dest, args.repo);
668
+ }
643
669
  console.log(
644
670
  `installed\t${operation.agentId}\t${operation.variantKey}\t${path.relative(args.repo, operation.dest)}`
645
671
  );
@@ -212,6 +212,239 @@ const PROVIDERS = {
212
212
 
213
213
  const catalog = JSON.parse(readFileSync(catalogPath, "utf8"));
214
214
 
215
+ // --- Dynamic provider discovery and derivation ---
216
+
217
+ /** Special-case display name mappings for providers not in PROVIDERS. */
218
+ const DISPLAY_NAME_OVERRIDES = {
219
+ dotnet: ".NET",
220
+ hr: "HR",
221
+ fluxcd: "FluxCD",
222
+ argocd: "ArgoCD",
223
+ opentelemetry: "OpenTelemetry",
224
+ "cert-manager": "Cert-Manager",
225
+ "multi-cloud": "Multi-Cloud",
226
+ };
227
+
228
+ /** Pre-authored keyword sets for derived providers. */
229
+ const DERIVED_KEYWORDS = {
230
+ argocd: ["argocd", "gitops", "progressive-delivery", "application-sync"],
231
+ dotnet: ["dotnet", "csharp", "aspnet-core", "ef-core", "nuget"],
232
+ marketing: ["marketing-governance", "consent-compliance", "advertising-fairness", "email-authentication"],
233
+ hr: ["hr-governance", "employment-risk", "compensation-equity", "recruiting"],
234
+ legal: ["legal-risk", "contract-review", "privacy-compliance", "regulatory"],
235
+ generic: ["test-quality", "ci-pipeline", "helm-chart", "manifest-review"],
236
+ "multi-cloud": ["finops", "cloud-pricing", "cost-optimization", "reserved-instances"],
237
+ backstage: ["backstage", "scaffolder", "software-templates", "developer-portal"],
238
+ "cert-manager": ["cert-manager", "x509", "certificate-lifecycle", "pki"],
239
+ cilium: ["cilium", "network-policy", "ebpf", "cluster-mesh"],
240
+ falco: ["falco", "runtime-threat", "syscall-rules", "container-security"],
241
+ fluxcd: ["fluxcd", "gitops", "kustomization", "helm-release"],
242
+ istio: ["istio", "service-mesh", "ambient-mesh", "mtls"],
243
+ kyverno: ["kyverno", "admission-policy", "cluster-policy", "policy-enforcement"],
244
+ opentelemetry: ["opentelemetry", "otel-collector", "tracing", "observability-pipeline"],
245
+ prometheus: ["prometheus", "alertmanager", "metrics-cardinality", "scrape-config"],
246
+ sigstore: ["sigstore", "cosign", "supply-chain-integrity", "image-signing"],
247
+ };
248
+
249
+ /**
250
+ * Discover all unique providers from the catalog where at least one agent
251
+ * has 'kiro' in its harnesses array.
252
+ */
253
+ function discoverKiroProviders() {
254
+ const providers = new Set();
255
+ for (const entry of catalog) {
256
+ if (
257
+ entry.type === "agent" &&
258
+ Array.isArray(entry.harnesses) &&
259
+ entry.harnesses.includes("kiro")
260
+ ) {
261
+ providers.add(entry.provider);
262
+ }
263
+ }
264
+ return [...providers].sort();
265
+ }
266
+
267
+ /**
268
+ * Title-case a provider name, handling special cases.
269
+ */
270
+ function titleCaseProvider(provider) {
271
+ if (DISPLAY_NAME_OVERRIDES[provider]) return DISPLAY_NAME_OVERRIDES[provider];
272
+ return provider
273
+ .split("-")
274
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
275
+ .join("-");
276
+ }
277
+
278
+ /**
279
+ * Derive a topic summary from agent IDs for description generation.
280
+ */
281
+ function deriveTopics(entries) {
282
+ const topics = entries
283
+ .map((e) => e.id)
284
+ .filter((id) => !id.endsWith("-maestro-agent"))
285
+ .map((id) => {
286
+ // Strip provider prefix and -agent / -review-agent suffix
287
+ let topic = id
288
+ .replace(/-review-agent$/, "")
289
+ .replace(/-agent$/, "")
290
+ .replace(/-run-agent$/, "");
291
+ // Remove known provider prefixes
292
+ const prefixes = [
293
+ "dotnet-", "hr-", "legal-", "marketing-", "finops-",
294
+ "argocd-", "backstage-", "cert-manager-", "cilium-",
295
+ "falco-", "fluxcd-", "istio-", "kyverno-",
296
+ "opentelemetry-", "prometheus-", "sigstore-",
297
+ ];
298
+ for (const pfx of prefixes) {
299
+ if (topic.startsWith(pfx)) {
300
+ topic = topic.slice(pfx.length);
301
+ break;
302
+ }
303
+ }
304
+ return topic.replace(/-/g, " ");
305
+ })
306
+ .slice(0, 4);
307
+ return topics.join(", ");
308
+ }
309
+
310
+ /**
311
+ * Auto-generate steering content for a provider NOT in the hardcoded
312
+ * PROVIDERS object.
313
+ */
314
+ function deriveProviderConfig(provider, catalogEntries) {
315
+ const displayLabel = titleCaseProvider(provider);
316
+ const displayName = `Vanguard Frontier \u2014 ${displayLabel}`;
317
+
318
+ const entries = catalogEntries.filter(
319
+ (e) => e.type === "agent" && e.provider === provider,
320
+ );
321
+ const maestro = entries.find((e) => e.id.endsWith("-maestro-agent"));
322
+ const liveGuards = entries.filter((e) => /-live-/.test(e.id));
323
+
324
+ // Build description (max 3 sentences)
325
+ let description;
326
+ if (maestro && entries.length > 2) {
327
+ const topics = deriveTopics(entries);
328
+ description = `Curated ${displayLabel} agents for ${topics}. Routes via ${maestro.id} to specialist agents based on task scope. Static review only; no live mutations.`;
329
+ } else if (entries.length === 1) {
330
+ // Single agent, no maestro
331
+ const summary = entries[0].summary || "";
332
+ // Split into sentences - skip "Agent for <id>." prefix if present
333
+ const sentences = summary.split(/(?<=[.!?])\s/);
334
+ let useSentence = sentences[0] || "";
335
+ if (/^Agent for\s/i.test(useSentence) && sentences.length > 1) {
336
+ useSentence = sentences[1];
337
+ }
338
+ // Remove trailing period for reassembly
339
+ useSentence = useSentence.replace(/\.$/, "");
340
+ // Strip leading "Static review of" / "Review" prefix to avoid doubling
341
+ let core = useSentence
342
+ .replace(/^Static,?\s+evidence-gated\s+review\s+of\s+/i, "")
343
+ .replace(/^Static\s+review\s+of\s+/i, "")
344
+ .replace(/^Review\s+(a\s+)?/i, "");
345
+ // Truncate if too long, respecting word boundaries
346
+ if (core.length > 120) {
347
+ const truncated = core.substring(0, 117);
348
+ const lastSpace = truncated.lastIndexOf(" ");
349
+ core = (lastSpace > 0 ? truncated.substring(0, lastSpace) : truncated) + "...";
350
+ }
351
+ const sep = core.endsWith("...") ? " " : ". ";
352
+ description = `Reviews ${core.charAt(0).toLowerCase() + core.slice(1)}${sep}Static review only; no live mutations.`;
353
+ } else {
354
+ // Multiple agents, no maestro
355
+ const topics = deriveTopics(entries);
356
+ description = `Curated ${displayLabel} review agents covering ${topics}. Reference agents directly under agents/${provider}/. Static review only; no live mutations.`;
357
+ }
358
+
359
+ // Keywords
360
+ const keywords = DERIVED_KEYWORDS[provider] || [
361
+ provider,
362
+ "static-review",
363
+ "configuration-audit",
364
+ "best-practices",
365
+ ];
366
+
367
+ // Invariants
368
+ const invariants = [];
369
+ if (liveGuards.length > 0) {
370
+ invariants.push(
371
+ `Live-guard agents (${provider}-live-*) must never be auto-dispatched; require explicit approval and rollback plan.`,
372
+ );
373
+ }
374
+ if (maestro) {
375
+ invariants.push(
376
+ `Route all tasks through ${maestro.id} for proper classification and dispatch.`,
377
+ );
378
+ }
379
+ invariants.push(
380
+ "Static review only -- agents analyze configuration and provide findings without mutating live systems.",
381
+ );
382
+ // Add domain-specific invariants
383
+ if (provider === "dotnet") {
384
+ invariants.push("Review covers language runtime, frameworks, data access, testing, and supply-chain integrity.");
385
+ } else if (provider === "hr") {
386
+ invariants.push("All findings must respect employee privacy and data-minimization principles.");
387
+ } else if (provider === "legal") {
388
+ invariants.push("Agents provide risk-flagging only; output is not legal advice and does not create attorney-client privilege.");
389
+ } else if (provider === "marketing") {
390
+ invariants.push("Review covers consent, privacy, fairness, and regulatory compliance for marketing systems.");
391
+ } else if (provider === "multi-cloud") {
392
+ invariants.push("Cost recommendations are estimates based on public pricing; verify against actual billing before acting.");
393
+ } else if (provider === "generic") {
394
+ invariants.push("Agents are provider-agnostic and focus on CI, Helm, manifest, and test-quality patterns.");
395
+ } else if (provider === "argocd") {
396
+ invariants.push("Sync and rollout strategies must be validated against the target cluster GitOps workflow.");
397
+ } else if (provider === "backstage") {
398
+ invariants.push("Template parameters and scaffolder actions must be reviewed for injection and secret-exposure risks.");
399
+ } else if (provider === "cert-manager") {
400
+ invariants.push("Certificate renewal windows and issuer trust chains must be validated before any policy change.");
401
+ } else if (provider === "cilium") {
402
+ invariants.push("Network policies must be reviewed for unintended traffic blocking across namespaces and cluster-mesh endpoints.");
403
+ } else if (provider === "falco") {
404
+ invariants.push("Rule changes must be evaluated for false-positive rate impact on production alerting.");
405
+ } else if (provider === "fluxcd") {
406
+ invariants.push("Kustomization and HelmRelease reconciliation intervals must align with the GitOps change cadence.");
407
+ } else if (provider === "istio") {
408
+ invariants.push("Service mesh policies affect traffic routing cluster-wide; review blast radius before changes.");
409
+ } else if (provider === "kyverno") {
410
+ invariants.push("Cluster-scoped policies can reject legitimate workloads; validate against existing deployments before applying.");
411
+ } else if (provider === "opentelemetry") {
412
+ invariants.push("Collector pipeline changes affect observability for all instrumented services; review cardinality impact.");
413
+ } else if (provider === "prometheus") {
414
+ invariants.push("Alerting rule and scrape config changes affect monitoring coverage; review for metric-name collisions.");
415
+ } else if (provider === "sigstore") {
416
+ invariants.push("Supply-chain policy changes can block valid deployments; verify cosign keyless trust roots before enforcement.");
417
+ }
418
+
419
+ return { displayName, description, keywords, invariants };
420
+ }
421
+
422
+ /**
423
+ * Build a merged map combining hand-authored PROVIDERS with auto-derived
424
+ * entries for all kiro-enabled providers in the catalog.
425
+ */
426
+ function buildMergedProviders() {
427
+ const kiroProviders = discoverKiroProviders();
428
+ const merged = {};
429
+
430
+ for (const provider of kiroProviders) {
431
+ if (PROVIDERS[provider]) {
432
+ merged[provider] = PROVIDERS[provider];
433
+ } else {
434
+ merged[provider] = deriveProviderConfig(provider, catalog);
435
+ }
436
+ }
437
+
438
+ // Sort alphabetically for deterministic output
439
+ const sorted = {};
440
+ for (const key of Object.keys(merged).sort()) {
441
+ sorted[key] = merged[key];
442
+ }
443
+ return sorted;
444
+ }
445
+
446
+ const allProviders = buildMergedProviders();
447
+
215
448
  function summarize(provider) {
216
449
  const entries = catalog.filter(
217
450
  (e) => e.type === "agent" && e.provider === provider,
@@ -254,7 +487,9 @@ function renderPower(provider, cfg) {
254
487
 
255
488
  const adapterNote =
256
489
  kiroAvailable === total
257
- ? `All ${total} agents in this provider ship a Kiro adapter (\`harnesses/kiro-ide.agent.md\`, \`kiro-cli.agent.json\`).`
490
+ ? total === 1
491
+ ? `The single agent in this provider ships a Kiro adapter (\`harnesses/kiro-ide.agent.md\`, \`kiro-cli.agent.json\`).`
492
+ : `All ${total} agents in this provider ship a Kiro adapter (\`harnesses/kiro-ide.agent.md\`, \`kiro-cli.agent.json\`).`
258
493
  : kiroAvailable === 0
259
494
  ? `This provider's ${total} agents do not yet ship Kiro adapters — this Power supplies steering content only. Use \`npx vfa-export-agents --platform kiro --provider ${provider}\` from the npm package once Kiro adapters land.`
260
495
  : `${kiroAvailable} of ${total} agents in this provider ship a Kiro adapter; the rest provide steering context only.`;
@@ -273,7 +508,9 @@ function renderPower(provider, cfg) {
273
508
  "",
274
509
  maestroLine,
275
510
  "",
276
- "Use the maestro as the entry point: classify the task, then dispatch to one specialist or a parallel team of specialists. Never have the maestro itself execute a live mutation.",
511
+ maestro
512
+ ? "Use the maestro as the entry point: classify the task, then dispatch to one specialist or a parallel team of specialists. Never have the maestro itself execute a live mutation."
513
+ : "Reference agents directly from agents/" + provider + "/ without maestro-based routing.",
277
514
  "",
278
515
  "## Live-guard agents (gate_mode only)",
279
516
  "",
@@ -299,10 +536,114 @@ function renderPower(provider, cfg) {
299
536
  return frontmatter + body;
300
537
  }
301
538
 
539
+ function renderReadme() {
540
+ const providerKeys = Object.keys(allProviders);
541
+ const count = providerKeys.length;
542
+ const tree = providerKeys
543
+ .map((p, i) => {
544
+ const prefix = i < count - 1 ? "├──" : "└──";
545
+ return `${prefix} vanguard-${p}/POWER.md`;
546
+ })
547
+ .join("\n");
548
+
549
+ return `# \`powers/\` — Kiro Powers
550
+
551
+ This directory holds **${count} Kiro Powers** for \`vanguard-frontier-agentic\`, one
552
+ per cloud/platform/IaC provider. Each Power is a directory containing a
553
+ \`POWER.md\` file with strict-5 frontmatter and steering content.
554
+
555
+ ## What's in here
556
+
557
+ \`\`\`
558
+ powers/
559
+ ${tree}
560
+ \`\`\`
561
+
562
+ Each \`POWER.md\` declares:
563
+
564
+ - **Frontmatter (strict-5):** \`name\`, \`displayName\`, \`description\` (≤ 3
565
+ sentences), \`keywords\` (specific, non-broad), \`author\`. **No other fields
566
+ permitted** by Kiro spec.
567
+ - **Body steering:** when to engage, routing pattern (\`<provider>-maestro\`),
568
+ live-mutation discipline, provider-specific invariants (e.g. MLPS 2.0 for
569
+ Alibaba/Huawei, Enterprise Project vs IAM scope for Huawei, account-ID
570
+ /region confirmation for AWS).
571
+
572
+ ## How users install
573
+
574
+ Kiro Powers don't have a one-command marketplace install — the Powers panel
575
+ is per-Power directory add. Users clone the repo and add each Power they
576
+ need via the Kiro UI:
577
+
578
+ \`\`\`bash
579
+ # 1. Clone the repo
580
+ git clone https://github.com/Raishin/vanguard-frontier-agentic
581
+ cd vanguard-frontier-agentic
582
+ \`\`\`
583
+
584
+ \`\`\`text
585
+ 2. In Kiro:
586
+ Open the Powers panel → "Add Custom Power" → "Local Directory"
587
+ Paste the absolute path to the Power(s) you need:
588
+ /absolute/path/to/vanguard-frontier-agentic/powers/vanguard-aws
589
+ /absolute/path/to/vanguard-frontier-agentic/powers/vanguard-kubernetes
590
+ Repeat for each provider you work with.
591
+ \`\`\`
592
+
593
+ ## How to update
594
+
595
+ \`\`\`bash
596
+ # Regenerate the ${count} Powers from catalog/agents.json + per-provider config:
597
+ npm run kiro-powers:write
598
+
599
+ # Then verify everything is in sync:
600
+ npm run validate:kiro-powers
601
+ \`\`\`
602
+
603
+ The \`validate\` chain runs \`validate:kiro-powers\` automatically. The
604
+ validator enforces:
605
+
606
+ - strict-5 frontmatter (any extra field fails)
607
+ - lowercase kebab-case names
608
+ - name matches directory name
609
+ - description ≤ 3 sentences (decimal-aware — "MLPS 2.0" doesn't count as a
610
+ sentence break)
611
+ - non-empty keywords list, no broad terms (\`cloud\`, \`devops\`, \`code\`,
612
+ \`agent\`, \`ml\`, etc.) per Kiro's anti-false-activation guidance
613
+ - generator in sync (\`--check\`)
614
+
615
+ ## Schema references (official Kiro docs)
616
+
617
+ - **Kiro Powers repo:** <https://github.com/kirodotdev/powers>
618
+ - **POWER.md frontmatter spec:**
619
+ <https://github.com/kirodotdev/powers/blob/main/power-builder/POWER.md>
620
+ - **Interactive power builder:**
621
+ <https://github.com/kirodotdev/powers/blob/main/power-builder/steering/interactive.md>
622
+ - **Testing a power locally:**
623
+ <https://github.com/kirodotdev/powers/blob/main/power-builder/steering/testing.md>
624
+ - **Kiro IDE:** <https://kiro.dev/>
625
+
626
+ ## Design notes
627
+
628
+ - **One Power per provider, not one mega-Power** — Kiro docs warn that
629
+ broad keywords trigger false activations across unrelated tasks. One
630
+ narrowly-scoped Power per provider keeps activation precise:
631
+ \`vanguard-alibaba\` activates on Alibaba Cloud work only; \`vanguard-aws\`
632
+ never activates on Azure questions.
633
+ - **Hetzner and Contabo Powers exist** even though their agents don't yet
634
+ ship Kiro adapter files (their \`harnesses: [codex, claude-code]\`). Powers
635
+ are steering-first — the steering content stands alone. When their Kiro
636
+ adapter files land, the Powers will gain agent-routing as well.
637
+ - **No \`version\`, \`repository\`, \`license\`, or \`tags\`** — Kiro spec
638
+ explicitly forbids these fields in frontmatter. The validator fails on
639
+ any extra field.
640
+ `;
641
+ }
642
+
302
643
  const errors = [];
303
644
  const written = [];
304
645
 
305
- for (const [provider, cfg] of Object.entries(PROVIDERS)) {
646
+ for (const [provider, cfg] of Object.entries(allProviders)) {
306
647
  const dir = join(powersRoot, `vanguard-${provider}`);
307
648
  const file = join(dir, "POWER.md");
308
649
  const next = renderPower(provider, cfg);
@@ -322,15 +663,29 @@ for (const [provider, cfg] of Object.entries(PROVIDERS)) {
322
663
  }
323
664
  }
324
665
 
666
+ // Generate/check README.md
667
+ const readmePath = join(powersRoot, "README.md");
668
+ const nextReadme = renderReadme();
669
+ if (check) {
670
+ if (!existsSync(readmePath)) {
671
+ errors.push(`${relative(repoRoot, readmePath)} is missing`);
672
+ } else if (readFileSync(readmePath, "utf8") !== nextReadme) {
673
+ errors.push(`${relative(repoRoot, readmePath)} is stale; run npm run kiro-powers:write`);
674
+ }
675
+ } else {
676
+ writeFileSync(readmePath, nextReadme);
677
+ written.push(relative(repoRoot, readmePath));
678
+ }
679
+
325
680
  if (check) {
326
681
  if (errors.length) {
327
682
  errors.forEach((e) => console.error(`ERROR: ${e}`));
328
683
  process.exit(1);
329
684
  }
330
685
  console.log(
331
- `OK: ${Object.keys(PROVIDERS).length} Kiro Powers are in sync`,
686
+ `OK: ${Object.keys(allProviders).length} Kiro Powers are in sync`,
332
687
  );
333
688
  } else {
334
- console.log(`OK: wrote ${written.length} Kiro Powers`);
689
+ console.log(`OK: wrote ${written.length} Kiro Powers (+ README.md)`);
335
690
  written.forEach((f) => console.log(` ${f}`));
336
691
  }