agent-skills-standard 1.7.2 → 1.8.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.
package/README.md CHANGED
@@ -82,7 +82,8 @@ Every skill delivered by this CLI is audited for its footprint in the AI's conte
82
82
 
83
83
  | Version | Date | Skills | Avg Tokens | Savings (%) | Report |
84
84
  | --- | --- | --- | --- | --- | --- |
85
- | v1.7.3 | 2026-02-25 | 220 | 413 | 89% | [Report](benchmarks/archive/v1.7.3.md) |
85
+ | v1.8.0 | 2026-03-02 | 228 | 443 | 88% | [Report](benchmarks/archive/v1.8.0.md) |
86
+ | v1.7.3 | 2026-02-25 | 222 | 418 | 89% | [Report](benchmarks/archive/v1.7.3.md) |
86
87
  | v1.7.2 | 2026-02-25 | 220 | 413 | 89% | [Report](benchmarks/archive/v1.7.2.md) |
87
88
 
88
89
  ## � Token Economy & Optimization
package/dist/index.js CHANGED
@@ -267,56 +267,68 @@ const SKILL_DETECTION_REGISTRY = {
267
267
  [Framework.Android]: [
268
268
  {
269
269
  id: "compose",
270
- packages: ["androidx.compose.ui"]
270
+ packages: ["androidx.compose.ui"],
271
+ files: ["android", "build.gradle", "build.gradle.kts"]
271
272
  },
272
273
  {
273
274
  id: "navigation",
274
- packages: ["androidx.navigation:navigation-compose"]
275
+ packages: ["androidx.navigation:navigation-compose"],
276
+ files: ["android", "build.gradle", "build.gradle.kts"]
275
277
  },
276
278
  {
277
279
  id: "legacy-navigation",
278
280
  packages: [
279
281
  "androidx.navigation:navigation-fragment",
280
282
  "androidx.navigation:navigation-ui"
281
- ]
283
+ ],
284
+ files: ["android", "build.gradle", "build.gradle.kts"]
282
285
  },
283
286
  {
284
287
  id: "di",
285
- packages: ["hilt-android", "dagger-android"]
288
+ packages: ["hilt-android", "dagger-android"],
289
+ files: ["android", "build.gradle", "build.gradle.kts"]
286
290
  },
287
291
  {
288
292
  id: "persistence",
289
- packages: ["androidx.room:room-runtime"]
293
+ packages: ["androidx.room:room-runtime"],
294
+ files: ["android", "build.gradle", "build.gradle.kts"]
290
295
  },
291
296
  {
292
297
  id: "networking",
293
- packages: ["retrofit"]
298
+ packages: ["retrofit"],
299
+ files: ["android", "build.gradle", "build.gradle.kts"]
294
300
  },
295
301
  {
296
302
  id: "concurrency",
297
- packages: ["kotlinx-coroutines-android"]
303
+ packages: ["kotlinx-coroutines-android"],
304
+ files: ["android", "build.gradle", "build.gradle.kts"]
298
305
  }
299
306
  ],
300
307
  [Framework.iOS]: [
301
308
  {
302
309
  id: "networking",
303
- packages: ["Alamofire", "Moya"]
310
+ packages: ["Alamofire", "Moya"],
311
+ files: ["ios", "Podfile", "Package.swift"]
304
312
  },
305
313
  {
306
314
  id: "dependency-injection",
307
- packages: ["Swinject", "Resolver"]
315
+ packages: ["Swinject", "Resolver"],
316
+ files: ["ios", "Podfile", "Package.swift"]
308
317
  },
309
318
  {
310
319
  id: "persistence",
311
- packages: ["Realm", "CoreData", "SQLite.swift"]
320
+ packages: ["Realm", "CoreData", "SQLite.swift"],
321
+ files: ["ios", "Podfile", "Package.swift"]
312
322
  },
313
323
  {
314
324
  id: "state-management",
315
- packages: ["ComposableArchitecture", "CombineRuntime"]
325
+ packages: ["ComposableArchitecture", "CombineRuntime"],
326
+ files: ["ios", "Podfile", "Package.swift"]
316
327
  },
317
328
  {
318
329
  id: "ui-navigation",
319
- packages: ["Coordinator", "Router"]
330
+ packages: ["Coordinator", "Router"],
331
+ files: ["ios", "Podfile", "Package.swift"]
320
332
  }
321
333
  ],
322
334
  [Framework.ReactNative]: [
@@ -364,6 +376,65 @@ const SKILL_DETECTION_REGISTRY = {
364
376
  // Always present, but used for sub-skill mapping
365
377
  }
366
378
  ],
379
+ [Framework.NextJS]: [
380
+ {
381
+ id: "pages-router",
382
+ packages: [],
383
+ files: ["pages", "src/pages"]
384
+ },
385
+ {
386
+ id: "server-components",
387
+ packages: [],
388
+ files: ["app", "src/app"]
389
+ },
390
+ {
391
+ id: "server-actions",
392
+ packages: [],
393
+ files: ["app", "src/app"]
394
+ },
395
+ {
396
+ id: "data-fetching",
397
+ packages: [],
398
+ files: ["app", "src/app"]
399
+ },
400
+ {
401
+ id: "i18n",
402
+ packages: ["next-intl", "react-intl", "next-translate", "i18next"],
403
+ files: [
404
+ "middleware.ts",
405
+ "app/[lang]",
406
+ "pages/[locale]",
407
+ "next.config.js"
408
+ ]
409
+ }
410
+ ],
411
+ [Framework.React]: [
412
+ {
413
+ id: "styling",
414
+ packages: ["tailwind", "antd", "sass", "styled-components", "emotion"]
415
+ },
416
+ {
417
+ id: "state-management",
418
+ packages: [
419
+ "redux",
420
+ "@reduxjs/toolkit",
421
+ "zustand",
422
+ "mobx",
423
+ "recoil",
424
+ "jotai"
425
+ ]
426
+ },
427
+ {
428
+ id: "testing",
429
+ packages: [
430
+ "jest",
431
+ "vitest",
432
+ "@testing-library/react",
433
+ "cypress",
434
+ "playwright"
435
+ ]
436
+ }
437
+ ],
367
438
  database: [
368
439
  {
369
440
  id: "postgresql",
@@ -385,11 +456,38 @@ const BACKEND_FRAMEWORKS = [
385
456
  Framework.SpringBoot,
386
457
  Framework.Laravel
387
458
  ];
459
+ const FRONTEND_FRAMEWORKS = [
460
+ Framework.React,
461
+ Framework.NextJS,
462
+ Framework.Angular
463
+ ];
464
+ const MOBILE_FRAMEWORKS = [
465
+ Framework.Flutter,
466
+ Framework.Android,
467
+ Framework.iOS,
468
+ Framework.ReactNative
469
+ ];
470
+ const FRONTEND_REACT_FRAMEWORKS = [
471
+ Framework.NextJS,
472
+ Framework.ReactNative
473
+ ];
474
+ const COMMON_SKILL_EXCLUDES = {
475
+ backend: ["accessibility", "mobile-animation", "mobile-ux-core"],
476
+ frontend: ["observability", "mobile-animation", "mobile-ux-core"],
477
+ mobile: ["accessibility", "api-design", "observability"]
478
+ };
479
+ function getFrameworkType(framework) {
480
+ if (BACKEND_FRAMEWORKS.includes(framework)) return "backend";
481
+ if (MOBILE_FRAMEWORKS.includes(framework)) return "mobile";
482
+ if (FRONTEND_FRAMEWORKS.includes(framework)) return "frontend";
483
+ return null;
484
+ }
388
485
  const DEFAULT_REGISTER = "https://github.com/HoangNguyen0403/agent-skills-standard";
389
486
  const DEFAULT_WORKFLOWS = [
390
487
  "code-review",
391
488
  "codebase-review",
392
- "plan-feature"
489
+ "plan-feature",
490
+ "skill-benchmark"
393
491
  ];
394
492
  const getAgentDefinition = (id) => {
395
493
  switch (id) {
@@ -701,8 +799,10 @@ class ConfigService {
701
799
  skills[framework] = {
702
800
  ref: metadata.categories?.[framework]?.version ? `${metadata.categories[framework].tag_prefix || ""}${metadata.categories[framework].version}` : "main"
703
801
  };
704
- if (framework === "react-native" && metadata.categories?.["react"]) {
705
- skills[framework].include = ["react/hooks", "react/component-patterns"];
802
+ if (FRONTEND_REACT_FRAMEWORKS.includes(framework) && metadata.categories?.["react"]) {
803
+ skills["react"] = {
804
+ ref: metadata.categories["react"].version ? `${metadata.categories["react"].tag_prefix || ""}${metadata.categories["react"].version}` : "main"
805
+ };
706
806
  }
707
807
  for (const lang of languages) {
708
808
  if (metadata.categories?.[lang]) {
@@ -712,8 +812,11 @@ class ConfigService {
712
812
  }
713
813
  }
714
814
  if (metadata.categories?.["common"]) {
815
+ const frameworkType = getFrameworkType(framework);
816
+ const commonExcludes = frameworkType ? COMMON_SKILL_EXCLUDES[frameworkType] : [];
715
817
  skills["common"] = {
716
- ref: `${metadata.categories["common"].tag_prefix || ""}${metadata.categories["common"].version}`
818
+ ref: `${metadata.categories["common"].tag_prefix || ""}${metadata.categories["common"].version}`,
819
+ ...commonExcludes.length > 0 && { exclude: commonExcludes }
717
820
  };
718
821
  }
719
822
  if (BACKEND_FRAMEWORKS.includes(framework) && metadata.categories?.["database"]) {
@@ -736,7 +839,7 @@ class ConfigService {
736
839
  * @param config The current configuration
737
840
  * @param projectDeps Current set of project dependencies
738
841
  */
739
- applyDependencyExclusions(config, projectDeps) {
842
+ applyDependencyExclusions(config, projectDeps, cwd = process.cwd()) {
740
843
  const depsArray = Array.from(projectDeps);
741
844
  for (const categoryId in config.skills) {
742
845
  const category = config.skills[categoryId];
@@ -744,7 +847,9 @@ class ConfigService {
744
847
  if (detections.length === 0) continue;
745
848
  const exclusions = new Set(category.exclude || []);
746
849
  for (const detection of detections) {
747
- if (!this.hasDependency(detection.packages, depsArray)) {
850
+ const hasDeps = this.hasDependency(detection.packages, depsArray);
851
+ const hasDirs = this.hasFiles(detection.files, cwd);
852
+ if (!hasDeps || !hasDirs) {
748
853
  exclusions.add(detection.id);
749
854
  }
750
855
  }
@@ -757,7 +862,7 @@ class ConfigService {
757
862
  * Automatically re-enables skills that were previously excluded if their dependencies
758
863
  * are now present in the project. Scans all categories.
759
864
  */
760
- reconcileDependencies(config, projectDeps) {
865
+ reconcileDependencies(config, projectDeps, cwd = process.cwd()) {
761
866
  const totalReenabled = [];
762
867
  const allKnownCategories = Object.keys(SKILL_DETECTION_REGISTRY);
763
868
  const depsArray = Array.from(projectDeps);
@@ -767,13 +872,25 @@ class ConfigService {
767
872
  if (detections.length === 0) continue;
768
873
  const isNewCategory = !category;
769
874
  if (isNewCategory) {
875
+ const frameworkDef = SUPPORTED_FRAMEWORKS.find(
876
+ (f) => f.id === categoryId
877
+ );
878
+ if (frameworkDef) {
879
+ const hasBaseDeps = frameworkDef.detectionDependencies ? this.hasDependency(frameworkDef.detectionDependencies, depsArray) : true;
880
+ const hasBaseFiles = this.hasFiles(frameworkDef.detectionFiles, cwd);
881
+ if (!hasBaseDeps && !hasBaseFiles) {
882
+ continue;
883
+ }
884
+ }
770
885
  const shouldEnableCategory = detections.some(
771
- (detection) => this.hasDependency(detection.packages, depsArray)
886
+ (detection) => this.hasDependency(detection.packages, depsArray) && this.hasFiles(detection.files, cwd)
772
887
  );
773
888
  if (shouldEnableCategory) {
774
889
  config.skills[categoryId] = { ref: "main" };
775
890
  category = config.skills[categoryId];
776
- const exclusions = detections.filter((d) => !this.hasDependency(d.packages, depsArray)).map((d) => d.id);
891
+ const exclusions = detections.filter(
892
+ (d) => !this.hasDependency(d.packages, depsArray) || !this.hasFiles(d.files, cwd)
893
+ ).map((d) => d.id);
777
894
  if (exclusions.length > 0) {
778
895
  category.exclude = exclusions;
779
896
  }
@@ -785,7 +902,7 @@ class ConfigService {
785
902
  const currentExclusions = new Set(category.exclude);
786
903
  const reenabled = [];
787
904
  for (const detection of detections) {
788
- if (currentExclusions.has(detection.id) && this.hasDependency(detection.packages, depsArray)) {
905
+ if (currentExclusions.has(detection.id) && this.hasDependency(detection.packages, depsArray) && this.hasFiles(detection.files, cwd)) {
789
906
  currentExclusions.delete(detection.id);
790
907
  reenabled.push(`${categoryId}/${detection.id}`);
791
908
  }
@@ -798,14 +915,32 @@ class ConfigService {
798
915
  return totalReenabled;
799
916
  }
800
917
  /**
801
- * Checks if any of the provided packages are present in the project dependencies.
918
+ * Checks if ANY of the required files exist in the project directory.
919
+ */
920
+ hasFiles(files, cwd) {
921
+ if (!files || files.length === 0) return true;
922
+ for (const file of files) {
923
+ if (fs.existsSync(path.join(cwd, file))) {
924
+ return true;
925
+ }
926
+ }
927
+ return false;
928
+ }
929
+ /**
930
+ * Checks if ANY of the required dependencies are found in the project.
802
931
  */
803
932
  hasDependency(packages, projectDeps) {
933
+ if (packages.length === 0) return true;
804
934
  return projectDeps.some(
805
935
  (d) => packages.some((pkg2) => {
806
936
  const depLower = d.toLowerCase();
807
937
  const pkgLower = pkg2.toLowerCase();
808
- return pkg2.length <= 3 ? depLower === pkgLower : depLower.includes(pkgLower);
938
+ if (depLower === pkgLower) return true;
939
+ if (pkg2.length <= 3) return false;
940
+ if (depLower.includes("/") && depLower.split("/").pop() === pkgLower)
941
+ return true;
942
+ const parts = depLower.split(/[-/_]/);
943
+ return parts.includes(pkgLower);
809
944
  })
810
945
  );
811
946
  }
@@ -1512,7 +1647,11 @@ class AgentBridgeService {
1512
1647
  "> [!IMPORTANT]",
1513
1648
  "> ALWAYS consult the consolidated index in **AGENTS.md** to identify relevant triggers before acting.",
1514
1649
  "",
1515
- "The `AGENTS.md` file contains mapping between project files and the specific agent skills located in the respective agent-specific folders (e.g., `.cursor/skills`, `.claude/skills`)."
1650
+ "The `AGENTS.md` file contains mapping between project files and the specific agent skills located in the respective agent-specific folders (e.g., `.cursor/skills`, `.claude/skills`).",
1651
+ "",
1652
+ "## Self-Learning Protocol",
1653
+ "",
1654
+ "At the end of any multi-step task with user corrections, load and run **[common/session-retrospective](/skills/common/session-retrospective/SKILL.md)** to capture skill gaps and prevent repeat rework."
1516
1655
  ].join("\n");
1517
1656
  for (const agentId of agents) {
1518
1657
  const config = getAgentDefinition(agentId);
@@ -1557,6 +1696,20 @@ applyTo: "**/*"
1557
1696
  }
1558
1697
  }
1559
1698
  class IndexGeneratorService {
1699
+ /**
1700
+ * Loads foundational composite rules from the registry's metadata.json.
1701
+ * Falls back to an empty map if the file is missing or malformed.
1702
+ */
1703
+ async loadFoundationalRules(baseDir) {
1704
+ try {
1705
+ const metaPath = path.join(baseDir, "metadata.json");
1706
+ const raw = await fs.readFile(metaPath, "utf8");
1707
+ const parsed = JSON.parse(raw);
1708
+ return parsed.foundational_composite_rules ?? {};
1709
+ } catch {
1710
+ return {};
1711
+ }
1712
+ }
1560
1713
  /**
1561
1714
  * Generates a markdown index of available skills across multiple categories.
1562
1715
  * @param baseDir The base directory containing categories and skills
@@ -1566,6 +1719,7 @@ class IndexGeneratorService {
1566
1719
  async generate(baseDir, frameworks) {
1567
1720
  const categories = Array.from(/* @__PURE__ */ new Set(["common", ...frameworks]));
1568
1721
  const entries = /* @__PURE__ */ new Set();
1722
+ const foundationalRules = await this.loadFoundationalRules(baseDir);
1569
1723
  for (const category of categories) {
1570
1724
  const categoryPath = path.join(baseDir, category);
1571
1725
  if (!await fs.pathExists(categoryPath)) continue;
@@ -1575,7 +1729,12 @@ class IndexGeneratorService {
1575
1729
  if (!await fs.pathExists(skillPath)) continue;
1576
1730
  const metadata = await this.parseSkill(skillPath);
1577
1731
  if (metadata) {
1578
- const entry = this.formatEntry(category, skill, metadata);
1732
+ const entry = this.formatEntry(
1733
+ category,
1734
+ skill,
1735
+ metadata,
1736
+ foundationalRules
1737
+ );
1579
1738
  entries.add(entry);
1580
1739
  }
1581
1740
  }
@@ -1601,6 +1760,13 @@ class IndexGeneratorService {
1601
1760
  "",
1602
1761
  "- **Skill Authority:** Loaded skills always override existing code patterns.",
1603
1762
  "- **Audit Before Write:** Audit every file write against the `common/feedback-reporter` skill.",
1763
+ "",
1764
+ "## **Reading This Index**",
1765
+ "",
1766
+ "Each entry lists `(triggers: ...)` conditions. Three trigger types:",
1767
+ "- **File globs** (`**/*.ts`) — activate when working with matching files.",
1768
+ "- **Keywords** (`auth`, `refactor`) — activate when these appear in the task.",
1769
+ "- **Composite** (`+other/skill`) — activate THIS skill whenever `other/skill` is also active. Used by foundational skills to co-load with their framework peers automatically.",
1604
1770
  ""
1605
1771
  ].join("\n");
1606
1772
  return `${header}
@@ -1628,13 +1794,36 @@ ${entries.join("\n")}
1628
1794
  return null;
1629
1795
  }
1630
1796
  }
1631
- formatEntry(category, skill, metadata) {
1797
+ /**
1798
+ * Derives foundational composite IDs for a skill based on registry-owned naming rules.
1799
+ * Only applies to non-common skills. Deduplicates against explicit composites.
1800
+ */
1801
+ deriveFoundationalComposites(category, skill, explicitComposites, rules) {
1802
+ if (category === "common") return [];
1803
+ const derived = [];
1804
+ for (const [foundational, patterns] of Object.entries(rules)) {
1805
+ const matches = patterns.some((p) => skill.includes(p));
1806
+ if (matches && !explicitComposites.includes(foundational)) {
1807
+ derived.push(foundational);
1808
+ }
1809
+ }
1810
+ return derived;
1811
+ }
1812
+ formatEntry(category, skill, metadata, foundationalRules = {}) {
1632
1813
  const id = `${category}/${skill}`;
1633
1814
  const prefix = metadata.priority.startsWith("P0") ? "🚨 " : "";
1815
+ const explicitComposites = metadata.triggers.composite || [];
1816
+ const autoComposites = this.deriveFoundationalComposites(
1817
+ category,
1818
+ skill,
1819
+ explicitComposites,
1820
+ foundationalRules
1821
+ );
1822
+ const allComposites = [...explicitComposites, ...autoComposites];
1634
1823
  const triggers = [
1635
1824
  ...metadata.triggers.files || [],
1636
1825
  ...metadata.triggers.keywords || [],
1637
- ...metadata.triggers.composite ? metadata.triggers.composite.map((c) => `+${c}`) : [],
1826
+ ...allComposites.length ? allComposites.map((c) => `+${c}`) : [],
1638
1827
  ...metadata.triggers.exclude ? metadata.triggers.exclude.map((e) => `!${e}`) : []
1639
1828
  ].join(", ");
1640
1829
  const triggerText = triggers ? ` (triggers: ${triggers})` : "";
@@ -1755,10 +1944,13 @@ class SyncService {
1755
1944
  } else if (config.workflows === void 0 || config.workflows === true) {
1756
1945
  const agents2 = await this.resolveTargetAgents(config);
1757
1946
  if (agents2.includes(Agent.Antigravity)) {
1758
- config.workflows = availableWorkflows;
1947
+ const defaultWorkflows = availableWorkflows.filter(
1948
+ (wf) => DEFAULT_WORKFLOWS.includes(wf)
1949
+ );
1950
+ config.workflows = defaultWorkflows;
1759
1951
  console.log(
1760
1952
  pc.yellow(
1761
- `✨ Workflows Initialized: Adding [${availableWorkflows.join(", ")}] to .skillsrc.`
1953
+ `✨ Workflows Initialized: Adding [${defaultWorkflows.join(", ")}] to .skillsrc.`
1762
1954
  )
1763
1955
  );
1764
1956
  changed = true;
@@ -2188,7 +2380,7 @@ class SyncCommand {
2188
2380
  }
2189
2381
  }
2190
2382
  }
2191
- const version = "1.7.2";
2383
+ const version = "1.8.0";
2192
2384
  const pkg = {
2193
2385
  version
2194
2386
  };
@@ -2706,7 +2898,7 @@ dotenv.config();
2706
2898
  const program = new commander.Command();
2707
2899
  program.name("agent-skills-standard").description(
2708
2900
  "A CLI to manage and sync AI agent skills for Cursor, Claude, Copilot, Windsurf, and more."
2709
- ).version("1.7.2");
2901
+ ).version("1.8.0");
2710
2902
  program.command("init").description("Initialize a .skillsrc configuration file interactively").action(async () => {
2711
2903
  const init = new InitCommand();
2712
2904
  await init.run();