@omnidev-ai/cli 0.11.0 → 0.12.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 (2) hide show
  1. package/dist/index.js +589 -54
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -22,10 +22,10 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
22
22
  import { run } from "@stricli/core";
23
23
 
24
24
  // src/lib/dynamic-app.ts
25
- import { existsSync as existsSync9 } from "node:fs";
25
+ import { existsSync as existsSync10 } from "node:fs";
26
26
  import { createRequire as createRequire2 } from "node:module";
27
27
  import { join as join9 } from "node:path";
28
- import { buildApplication, buildRouteMap as buildRouteMap5 } from "@stricli/core";
28
+ import { buildApplication, buildRouteMap as buildRouteMap6 } from "@stricli/core";
29
29
 
30
30
  // src/commands/add.ts
31
31
  import { existsSync as existsSync3 } from "node:fs";
@@ -357,11 +357,19 @@ async function runAddCap(flags, name) {
357
357
  }
358
358
  let capabilityId = name;
359
359
  if (!capabilityId) {
360
- const sourceValue = sourceType === "local" ? flags.local : flags.github;
361
- if (!sourceValue) {
362
- throw new Error("Unreachable: cannot infer capability ID");
360
+ if (flags.path && sourceType === "github") {
361
+ const pathParts = flags.path.split("/").filter(Boolean);
362
+ capabilityId = pathParts[pathParts.length - 1];
363
+ if (!capabilityId) {
364
+ capabilityId = "capability";
365
+ }
366
+ } else {
367
+ const sourceValue = sourceType === "local" ? flags.local : flags.github;
368
+ if (!sourceValue) {
369
+ throw new Error("Unreachable: cannot infer capability ID");
370
+ }
371
+ capabilityId = await inferCapabilityId(sourceValue, sourceType);
363
372
  }
364
- capabilityId = await inferCapabilityId(sourceValue, sourceType);
365
373
  console.log(` Inferred capability ID: ${capabilityId}`);
366
374
  }
367
375
  const config = await loadBaseConfig();
@@ -684,6 +692,7 @@ import {
684
692
  generateSkillTemplate,
685
693
  getEnabledCapabilities,
686
694
  loadCapabilityConfig,
695
+ loadLockFile,
687
696
  syncAgentConfiguration as syncAgentConfiguration2
688
697
  } from "@omnidev-ai/core";
689
698
  import { buildCommand as buildCommand2, buildRouteMap as buildRouteMap2 } from "@stricli/core";
@@ -694,10 +703,11 @@ function isValidCapabilityId(id) {
694
703
  }
695
704
 
696
705
  // src/commands/capability.ts
697
- async function runCapabilityList() {
706
+ async function runCapabilityList(flags = {}) {
698
707
  try {
699
708
  const enabledIds = await getEnabledCapabilities();
700
709
  const capabilityPaths = await discoverCapabilities();
710
+ const lockFile = await loadLockFile();
701
711
  if (capabilityPaths.length === 0) {
702
712
  console.log("No capabilities found.");
703
713
  console.log("");
@@ -713,9 +723,29 @@ async function runCapabilityList() {
713
723
  const isEnabled = enabledIds.includes(capConfig.capability.id);
714
724
  const status = isEnabled ? "✓ enabled" : "✗ disabled";
715
725
  const { id, name, version } = capConfig.capability;
726
+ const lockEntry = lockFile.capabilities[id];
716
727
  console.log(` ${status} ${name}`);
717
728
  console.log(` ID: ${id}`);
718
729
  console.log(` Version: ${version}`);
730
+ if (flags.verbose && lockEntry) {
731
+ console.log(` Source: ${lockEntry.source}`);
732
+ if (lockEntry.version_source) {
733
+ console.log(` Version from: ${lockEntry.version_source}`);
734
+ }
735
+ if (lockEntry.commit) {
736
+ console.log(` Commit: ${lockEntry.commit.substring(0, 7)}`);
737
+ }
738
+ if (lockEntry.content_hash) {
739
+ console.log(` Content hash: ${lockEntry.content_hash.substring(0, 12)}`);
740
+ }
741
+ if (lockEntry.ref) {
742
+ console.log(` Ref: ${lockEntry.ref}`);
743
+ }
744
+ if (lockEntry.updated_at) {
745
+ const date = new Date(lockEntry.updated_at);
746
+ console.log(` Updated: ${date.toLocaleString()}`);
747
+ }
748
+ }
719
749
  console.log("");
720
750
  } catch (error) {
721
751
  console.error(` ✗ Failed to load capability at ${path}:`, error);
@@ -765,6 +795,65 @@ async function runCapabilityDisable(_flags, name) {
765
795
  function toTitleCase(kebabCase) {
766
796
  return kebabCase.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
767
797
  }
798
+ function generatePackageJson(id) {
799
+ const pkg = {
800
+ name: `@capability/${id}`,
801
+ version: "0.1.0",
802
+ type: "module",
803
+ main: "dist/index.js",
804
+ scripts: {
805
+ build: "esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js",
806
+ clean: "rm -rf dist"
807
+ },
808
+ dependencies: {
809
+ "@omnidev-ai/core": "latest"
810
+ },
811
+ devDependencies: {
812
+ esbuild: "^0.20.0"
813
+ }
814
+ };
815
+ return JSON.stringify(pkg, null, "\t");
816
+ }
817
+ function generateIndexTs(id, name) {
818
+ return `/**
819
+ * ${name} Capability
820
+ *
821
+ * A programmatic capability with CLI commands.
822
+ */
823
+
824
+ import type { CapabilityExport } from "@omnidev-ai/core";
825
+ import { buildCommand } from "@stricli/core";
826
+
827
+ // Example command implementation
828
+ async function run${id.replace(/-/g, "").replace(/^./, (c) => c.toUpperCase())}(): Promise<void> {
829
+ console.log("Hello from ${name}!");
830
+ console.log("");
831
+ console.log("This is a programmatic capability.");
832
+ console.log("Edit index.ts to customize this command.");
833
+ }
834
+
835
+ // Build the main command
836
+ const ${id.replace(/-/g, "")}Command = buildCommand({
837
+ docs: {
838
+ brief: "${name} command",
839
+ },
840
+ parameters: {},
841
+ func: run${id.replace(/-/g, "").replace(/^./, (c) => c.toUpperCase())},
842
+ });
843
+
844
+ // Default export: Structured capability export
845
+ export default {
846
+ cliCommands: {
847
+ "${id}": ${id.replace(/-/g, "")}Command,
848
+ },
849
+ } satisfies CapabilityExport;
850
+ `;
851
+ }
852
+ function generateGitignore() {
853
+ return `dist/
854
+ node_modules/
855
+ `;
856
+ }
768
857
  async function runCapabilityNew(flags, capabilityId) {
769
858
  try {
770
859
  if (!existsSync4(".omni")) {
@@ -809,6 +898,11 @@ async function runCapabilityNew(flags, capabilityId) {
809
898
  mkdirSync4(hooksDir, { recursive: true });
810
899
  await writeFile5(join8(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
811
900
  await writeFile5(join8(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
901
+ if (flags.programmatic) {
902
+ await writeFile5(join8(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
903
+ await writeFile5(join8(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
904
+ await writeFile5(join8(capabilityDir, ".gitignore"), generateGitignore(), "utf-8");
905
+ }
812
906
  console.log(`✓ Created capability: ${name}`);
813
907
  console.log(` Location: ${capabilityDir}`);
814
908
  console.log("");
@@ -818,10 +912,26 @@ async function runCapabilityNew(flags, capabilityId) {
818
912
  console.log(" - rules/coding-standards.md");
819
913
  console.log(" - hooks/hooks.toml");
820
914
  console.log(" - hooks/example-hook.sh");
915
+ if (flags.programmatic) {
916
+ console.log(" - package.json");
917
+ console.log(" - index.ts");
918
+ console.log(" - .gitignore");
919
+ }
821
920
  console.log("");
822
- console.log("\uD83D\uDCA1 To add this capability as a local source, run:");
823
- console.log(` omnidev add cap --local ./${capabilityDir}`);
921
+ if (flags.programmatic) {
922
+ console.log("\uD83D\uDCA1 To build and use this capability:");
923
+ console.log(` cd ${capabilityDir}`);
924
+ console.log(" npm install && npm run build");
925
+ console.log(` cd -`);
926
+ console.log(` omnidev add cap --local ./${capabilityDir}`);
927
+ } else {
928
+ console.log("\uD83D\uDCA1 To add this capability as a local source, run:");
929
+ console.log(` omnidev add cap --local ./${capabilityDir}`);
930
+ }
824
931
  } catch (error) {
932
+ if (error instanceof Error && error.name === "ExitPromptError") {
933
+ process.exit(0);
934
+ }
825
935
  console.error("Error creating capability:", error);
826
936
  process.exit(1);
827
937
  }
@@ -833,9 +943,12 @@ var newCommand = buildCommand2({
833
943
 
834
944
  By default, creates the capability at capabilities/<id>. You can specify a custom path using the --path flag or interactively.
835
945
 
946
+ Use --programmatic to create a TypeScript capability with CLI commands, package.json, and esbuild setup.
947
+
836
948
  Examples:
837
- omnidev capability new my-cap # Prompts for path, defaults to capabilities/my-cap
838
- omnidev capability new my-cap --path ./caps/my # Uses ./caps/my directly`
949
+ omnidev capability new my-cap # Prompts for path, defaults to capabilities/my-cap
950
+ omnidev capability new my-cap --path ./caps/my # Uses ./caps/my directly
951
+ omnidev capability new my-cap --programmatic # Creates with TypeScript + CLI support`
839
952
  },
840
953
  parameters: {
841
954
  flags: {
@@ -844,6 +957,11 @@ Examples:
844
957
  brief: "Output path for the capability (skips interactive prompt)",
845
958
  parse: String,
846
959
  optional: true
960
+ },
961
+ programmatic: {
962
+ kind: "boolean",
963
+ brief: "Create a TypeScript capability with CLI commands and esbuild setup",
964
+ optional: true
847
965
  }
848
966
  },
849
967
  positional: {
@@ -863,11 +981,31 @@ Examples:
863
981
  });
864
982
  var listCommand = buildCommand2({
865
983
  docs: {
866
- brief: "List all discovered capabilities"
984
+ brief: "List all discovered capabilities",
985
+ fullDescription: `List all discovered capabilities with their status.
986
+
987
+ Use --verbose to show additional version information including:
988
+ - Source (git URL or file path)
989
+ - Where the version was detected from
990
+ - Commit hash (for git sources)
991
+ - Content hash (for file sources)
992
+ - Last update time
993
+
994
+ Examples:
995
+ omnidev capability list
996
+ omnidev capability list --verbose`
867
997
  },
868
- parameters: {},
869
- async func() {
870
- await runCapabilityList();
998
+ parameters: {
999
+ flags: {
1000
+ verbose: {
1001
+ kind: "boolean",
1002
+ brief: "Show detailed version information",
1003
+ optional: true
1004
+ }
1005
+ }
1006
+ },
1007
+ async func(flags) {
1008
+ await runCapabilityList(flags);
871
1009
  }
872
1010
  });
873
1011
  var enableCommand = buildCommand2({
@@ -1119,8 +1257,7 @@ import { buildCommand as buildCommand4 } from "@stricli/core";
1119
1257
  // src/prompts/provider.ts
1120
1258
  import { checkbox, confirm } from "@inquirer/prompts";
1121
1259
  var PROVIDER_GITIGNORE_FILES = {
1122
- claude: ["CLAUDE.md", ".claude/"],
1123
- "claude-code": ["CLAUDE.md", ".claude/"],
1260
+ "claude-code": ["CLAUDE.md", ".claude/", ".mcp.json"],
1124
1261
  cursor: [".cursor/"],
1125
1262
  codex: ["AGENTS.md", ".codex/"],
1126
1263
  opencode: [".opencode/"]
@@ -1549,17 +1686,398 @@ var providerRoutes = buildRouteMap4({
1549
1686
  }
1550
1687
  });
1551
1688
 
1552
- // src/commands/sync.ts
1689
+ // src/commands/security.ts
1553
1690
  import { existsSync as existsSync8 } from "node:fs";
1691
+ import {
1692
+ addSecurityAllow,
1693
+ buildCapabilityRegistry,
1694
+ formatScanResults,
1695
+ getAllSecurityAllows,
1696
+ readSecurityAllows,
1697
+ removeSecurityAllow,
1698
+ scanCapabilities
1699
+ } from "@omnidev-ai/core";
1700
+ import { buildCommand as buildCommand7, buildRouteMap as buildRouteMap5 } from "@stricli/core";
1701
+ var VALID_FINDING_TYPES = [
1702
+ "unicode_bidi",
1703
+ "unicode_zero_width",
1704
+ "unicode_control",
1705
+ "symlink_escape",
1706
+ "symlink_absolute",
1707
+ "suspicious_script",
1708
+ "binary_file"
1709
+ ];
1710
+ function isValidFindingType(type) {
1711
+ return VALID_FINDING_TYPES.includes(type);
1712
+ }
1713
+ async function filterAllowedFindings(summary, options = {}) {
1714
+ const state = await readSecurityAllows();
1715
+ const { includeLow = false } = options;
1716
+ const filteredResults = summary.results.map((result) => {
1717
+ const allows = state.allows[result.capabilityId] ?? [];
1718
+ const filteredFindings = result.findings.filter((finding) => {
1719
+ if (allows.includes(finding.type))
1720
+ return false;
1721
+ if (!includeLow && finding.severity === "low")
1722
+ return false;
1723
+ return true;
1724
+ });
1725
+ return {
1726
+ ...result,
1727
+ findings: filteredFindings,
1728
+ passed: filteredFindings.length === 0 || filteredFindings.every((f) => f.severity === "low")
1729
+ };
1730
+ });
1731
+ const findingsByType = {
1732
+ unicode_bidi: 0,
1733
+ unicode_zero_width: 0,
1734
+ unicode_control: 0,
1735
+ symlink_escape: 0,
1736
+ symlink_absolute: 0,
1737
+ suspicious_script: 0,
1738
+ binary_file: 0
1739
+ };
1740
+ const findingsBySeverity = {
1741
+ low: 0,
1742
+ medium: 0,
1743
+ high: 0,
1744
+ critical: 0
1745
+ };
1746
+ let totalFindings = 0;
1747
+ let capabilitiesWithFindings = 0;
1748
+ for (const result of filteredResults) {
1749
+ if (result.findings.length > 0) {
1750
+ capabilitiesWithFindings++;
1751
+ }
1752
+ for (const finding of result.findings) {
1753
+ totalFindings++;
1754
+ const currentTypeCount = findingsByType[finding.type] ?? 0;
1755
+ findingsByType[finding.type] = currentTypeCount + 1;
1756
+ const currentSeverityCount = findingsBySeverity[finding.severity] ?? 0;
1757
+ findingsBySeverity[finding.severity] = currentSeverityCount + 1;
1758
+ }
1759
+ }
1760
+ return {
1761
+ ...summary,
1762
+ results: filteredResults,
1763
+ totalFindings,
1764
+ capabilitiesWithFindings,
1765
+ findingsByType,
1766
+ findingsBySeverity,
1767
+ allPassed: filteredResults.every((r) => r.passed)
1768
+ };
1769
+ }
1770
+ function formatFindingsWithHints(summary) {
1771
+ const lines = [];
1772
+ lines.push("Security Scan Results");
1773
+ lines.push("=====================");
1774
+ lines.push("");
1775
+ if (summary.totalFindings === 0) {
1776
+ lines.push("No security issues found");
1777
+ return lines.join(`
1778
+ `);
1779
+ }
1780
+ lines.push(`Found ${summary.totalFindings} issue(s) in ${summary.capabilitiesWithFindings} capability(ies)`);
1781
+ lines.push("");
1782
+ if (summary.findingsBySeverity.critical > 0) {
1783
+ lines.push(` CRITICAL: ${summary.findingsBySeverity.critical}`);
1784
+ }
1785
+ if (summary.findingsBySeverity.high > 0) {
1786
+ lines.push(` HIGH: ${summary.findingsBySeverity.high}`);
1787
+ }
1788
+ if (summary.findingsBySeverity.medium > 0) {
1789
+ lines.push(` MEDIUM: ${summary.findingsBySeverity.medium}`);
1790
+ }
1791
+ if (summary.findingsBySeverity.low > 0) {
1792
+ lines.push(` LOW: ${summary.findingsBySeverity.low}`);
1793
+ }
1794
+ lines.push("");
1795
+ for (const result of summary.results) {
1796
+ if (result.findings.length === 0)
1797
+ continue;
1798
+ lines.push(`${result.capabilityId}:`);
1799
+ for (const finding of result.findings) {
1800
+ const location = finding.line ? `:${finding.line}${finding.column ? `:${finding.column}` : ""}` : "";
1801
+ const severity = finding.severity.toUpperCase().padEnd(8);
1802
+ lines.push(` [${severity}] ${finding.file}${location}`);
1803
+ lines.push(` ${finding.message}`);
1804
+ if (finding.details) {
1805
+ lines.push(` ${finding.details}`);
1806
+ }
1807
+ lines.push(` To allow: omnidev security allow ${result.capabilityId} ${finding.type}`);
1808
+ lines.push("");
1809
+ }
1810
+ }
1811
+ return lines.join(`
1812
+ `);
1813
+ }
1814
+ async function runSecurityIssues(flags = {}) {
1815
+ try {
1816
+ if (!existsSync8("omni.toml")) {
1817
+ console.log("No config file found");
1818
+ console.log(" Run: omnidev init");
1819
+ process.exit(1);
1820
+ }
1821
+ console.log("Scanning capabilities for security issues...");
1822
+ console.log("");
1823
+ const registry = await buildCapabilityRegistry();
1824
+ const capabilities = registry.getAllCapabilities();
1825
+ if (capabilities.length === 0) {
1826
+ console.log("No capabilities found to scan.");
1827
+ return;
1828
+ }
1829
+ const capabilityInfos = capabilities.map((cap) => ({
1830
+ id: cap.id,
1831
+ path: cap.path
1832
+ }));
1833
+ const summary = await scanCapabilities(capabilityInfos);
1834
+ const filteredSummary = await filterAllowedFindings(summary, {
1835
+ includeLow: flags.all ?? false
1836
+ });
1837
+ if (flags.verbose) {
1838
+ console.log(formatScanResults(filteredSummary, true));
1839
+ } else {
1840
+ console.log(formatFindingsWithHints(filteredSummary));
1841
+ }
1842
+ const hiddenCount = summary.totalFindings - filteredSummary.totalFindings;
1843
+ if (hiddenCount > 0) {
1844
+ const parts = [];
1845
+ if (!flags.all && summary.findingsBySeverity.low > 0) {
1846
+ parts.push(`${summary.findingsBySeverity.low} low-severity`);
1847
+ }
1848
+ const allowedCount = hiddenCount - (flags.all ? 0 : summary.findingsBySeverity.low);
1849
+ if (allowedCount > 0) {
1850
+ parts.push(`${allowedCount} allowed`);
1851
+ }
1852
+ if (parts.length > 0) {
1853
+ console.log(`(${parts.join(", ")} finding(s) hidden, use --all to show)`);
1854
+ console.log("");
1855
+ }
1856
+ }
1857
+ if (!filteredSummary.allPassed) {
1858
+ process.exit(1);
1859
+ }
1860
+ } catch (error) {
1861
+ console.error("Error scanning capabilities:", error);
1862
+ process.exit(1);
1863
+ }
1864
+ }
1865
+ async function runSecurityAllow(_flags, capabilityId, findingType) {
1866
+ try {
1867
+ if (!isValidFindingType(findingType)) {
1868
+ console.error(`Invalid finding type: '${findingType}'`);
1869
+ console.log("");
1870
+ console.log("Valid types:");
1871
+ for (const type of VALID_FINDING_TYPES) {
1872
+ console.log(` - ${type}`);
1873
+ }
1874
+ process.exit(1);
1875
+ }
1876
+ const added = await addSecurityAllow(capabilityId, findingType);
1877
+ if (added) {
1878
+ console.log(`Allowed ${findingType} for capability: ${capabilityId}`);
1879
+ } else {
1880
+ console.log(`Already allowed: ${findingType} for ${capabilityId}`);
1881
+ }
1882
+ } catch (error) {
1883
+ console.error("Error adding security allow:", error);
1884
+ process.exit(1);
1885
+ }
1886
+ }
1887
+ async function runSecurityDeny(_flags, capabilityId, findingType) {
1888
+ try {
1889
+ if (!isValidFindingType(findingType)) {
1890
+ console.error(`Invalid finding type: '${findingType}'`);
1891
+ console.log("");
1892
+ console.log("Valid types:");
1893
+ for (const type of VALID_FINDING_TYPES) {
1894
+ console.log(` - ${type}`);
1895
+ }
1896
+ process.exit(1);
1897
+ }
1898
+ const removed = await removeSecurityAllow(capabilityId, findingType);
1899
+ if (removed) {
1900
+ console.log(`Removed allow for ${findingType} on capability: ${capabilityId}`);
1901
+ } else {
1902
+ console.log(`No allow found for: ${findingType} on ${capabilityId}`);
1903
+ }
1904
+ } catch (error) {
1905
+ console.error("Error removing security allow:", error);
1906
+ process.exit(1);
1907
+ }
1908
+ }
1909
+ async function runSecurityListAllows() {
1910
+ try {
1911
+ const allows = await getAllSecurityAllows();
1912
+ if (allows.length === 0) {
1913
+ console.log("No security allows configured.");
1914
+ console.log("");
1915
+ console.log("Use 'omnidev security allow <cap-id> <type>' to add allows.");
1916
+ return;
1917
+ }
1918
+ console.log("Security Allows:");
1919
+ console.log("");
1920
+ const byCapability = {};
1921
+ for (const allow of allows) {
1922
+ const existing = byCapability[allow.capabilityId];
1923
+ if (existing) {
1924
+ existing.push(allow.findingType);
1925
+ } else {
1926
+ byCapability[allow.capabilityId] = [allow.findingType];
1927
+ }
1928
+ }
1929
+ for (const [capId, types] of Object.entries(byCapability)) {
1930
+ console.log(` ${capId}:`);
1931
+ for (const type of types) {
1932
+ console.log(` - ${type}`);
1933
+ }
1934
+ console.log("");
1935
+ }
1936
+ } catch (error) {
1937
+ console.error("Error listing security allows:", error);
1938
+ process.exit(1);
1939
+ }
1940
+ }
1941
+ var issuesCommand = buildCommand7({
1942
+ docs: {
1943
+ brief: "Scan capabilities for security issues",
1944
+ fullDescription: `Scan all enabled capabilities for security issues.
1945
+
1946
+ Security checks include:
1947
+ - Suspicious Unicode characters (bidi overrides, zero-width, control chars)
1948
+ - Symlinks that escape capability directories
1949
+ - Suspicious script patterns (curl|sh, eval, rm -rf /, etc.)
1950
+ - Binary files in content directories
1951
+
1952
+ By default, low-severity findings (like binary files) are hidden.
1953
+ Use --all to show all findings including low-severity ones.
1954
+
1955
+ Findings can be allowed using 'omnidev security allow <cap-id> <type>'.
1956
+
1957
+ Examples:
1958
+ omnidev security issues
1959
+ omnidev security issues --all
1960
+ omnidev security issues --verbose`
1961
+ },
1962
+ parameters: {
1963
+ flags: {
1964
+ verbose: {
1965
+ kind: "boolean",
1966
+ brief: "Show verbose output with raw format",
1967
+ optional: true
1968
+ },
1969
+ all: {
1970
+ kind: "boolean",
1971
+ brief: "Show all findings including low-severity",
1972
+ optional: true
1973
+ }
1974
+ }
1975
+ },
1976
+ async func(flags) {
1977
+ await runSecurityIssues(flags);
1978
+ }
1979
+ });
1980
+ var allowCommand = buildCommand7({
1981
+ docs: {
1982
+ brief: "Allow a security finding type for a capability",
1983
+ fullDescription: `Allow (ignore) a specific security finding type for a capability.
1984
+
1985
+ This stores the allow in .omni/security.json. Allowed findings are hidden
1986
+ from 'omnidev security issues' output.
1987
+
1988
+ Finding types:
1989
+ unicode_bidi - Bidirectional text override characters
1990
+ unicode_zero_width - Zero-width characters
1991
+ unicode_control - Suspicious control characters
1992
+ symlink_escape - Symlinks escaping capability directory
1993
+ symlink_absolute - Symlinks with absolute paths
1994
+ suspicious_script - Suspicious script patterns
1995
+ binary_file - Binary files in content directories
1996
+
1997
+ Examples:
1998
+ omnidev security allow my-capability unicode_bidi
1999
+ omnidev security allow my-capability suspicious_script`
2000
+ },
2001
+ parameters: {
2002
+ flags: {},
2003
+ positional: {
2004
+ kind: "tuple",
2005
+ parameters: [
2006
+ {
2007
+ brief: "Capability ID",
2008
+ parse: String
2009
+ },
2010
+ {
2011
+ brief: "Finding type to allow",
2012
+ parse: String
2013
+ }
2014
+ ]
2015
+ }
2016
+ },
2017
+ func: runSecurityAllow
2018
+ });
2019
+ var denyCommand = buildCommand7({
2020
+ docs: {
2021
+ brief: "Remove a security allow",
2022
+ fullDescription: `Remove a previously allowed security finding type.
2023
+
2024
+ This removes the allow from .omni/security.json. The finding will
2025
+ appear again in 'omnidev security issues' output.
2026
+
2027
+ Examples:
2028
+ omnidev security deny my-capability unicode_bidi
2029
+ omnidev security deny my-capability suspicious_script`
2030
+ },
2031
+ parameters: {
2032
+ flags: {},
2033
+ positional: {
2034
+ kind: "tuple",
2035
+ parameters: [
2036
+ {
2037
+ brief: "Capability ID",
2038
+ parse: String
2039
+ },
2040
+ {
2041
+ brief: "Finding type to remove allow for",
2042
+ parse: String
2043
+ }
2044
+ ]
2045
+ }
2046
+ },
2047
+ func: runSecurityDeny
2048
+ });
2049
+ var listAllowsCommand = buildCommand7({
2050
+ docs: {
2051
+ brief: "List all security allows"
2052
+ },
2053
+ parameters: {},
2054
+ async func() {
2055
+ await runSecurityListAllows();
2056
+ }
2057
+ });
2058
+ var securityRoutes = buildRouteMap5({
2059
+ routes: {
2060
+ issues: issuesCommand,
2061
+ allow: allowCommand,
2062
+ deny: denyCommand,
2063
+ "list-allows": listAllowsCommand
2064
+ },
2065
+ docs: {
2066
+ brief: "Security scanning and allows"
2067
+ }
2068
+ });
2069
+
2070
+ // src/commands/sync.ts
2071
+ import { existsSync as existsSync9 } from "node:fs";
1554
2072
  import {
1555
2073
  getActiveProfile as getActiveProfile3,
1556
2074
  loadConfig as loadConfig3,
1557
2075
  syncAgentConfiguration as syncAgentConfiguration6,
1558
2076
  writeEnabledProviders as writeEnabledProviders2
1559
2077
  } from "@omnidev-ai/core";
1560
- import { buildCommand as buildCommand7 } from "@stricli/core";
2078
+ import { buildCommand as buildCommand8 } from "@stricli/core";
1561
2079
  var PROVIDERS_STATE_PATH = ".omni/state/providers.json";
1562
- var syncCommand = buildCommand7({
2080
+ var syncCommand = buildCommand8({
1563
2081
  docs: {
1564
2082
  brief: "Manually sync all capabilities, roles, and instructions"
1565
2083
  },
@@ -1569,13 +2087,11 @@ var syncCommand = buildCommand7({
1569
2087
  }
1570
2088
  });
1571
2089
  async function runSync() {
1572
- console.log("Syncing OmniDev configuration...");
1573
- console.log("");
1574
2090
  try {
1575
2091
  const config = await loadConfig3();
1576
2092
  const activeProfile = await getActiveProfile3() ?? "default";
1577
2093
  let adapters = await getEnabledAdapters();
1578
- if (!existsSync8(PROVIDERS_STATE_PATH) || adapters.length === 0) {
2094
+ if (!existsSync9(PROVIDERS_STATE_PATH) || adapters.length === 0) {
1579
2095
  console.log("No providers configured yet. Select your provider(s):");
1580
2096
  const providerIds = await promptForProviders();
1581
2097
  await writeEnabledProviders2(providerIds);
@@ -1586,21 +2102,12 @@ async function runSync() {
1586
2102
  adapters = await initializeAdaptersForProviders(providerIds, ctx);
1587
2103
  console.log("");
1588
2104
  }
1589
- const result = await syncAgentConfiguration6({ silent: false, adapters });
2105
+ const providerNames = adapters.map((a) => a.displayName).join(", ") || "none";
2106
+ console.log(`Profile: ${activeProfile} | Providers: ${providerNames}`);
1590
2107
  console.log("");
1591
- console.log("✓ Sync completed successfully!");
1592
- console.log("");
1593
- console.log(`Profile: ${activeProfile}`);
1594
- console.log(`Capabilities: ${result.capabilities.join(", ") || "none"}`);
1595
- console.log(`Providers: ${adapters.map((a) => a.displayName).join(", ") || "none"}`);
1596
- console.log("");
1597
- console.log("Synced components:");
1598
- console.log(" • Capability registry");
1599
- console.log(" • Capability sync hooks");
1600
- console.log(" • .omni/.gitignore");
1601
- if (adapters.length > 0) {
1602
- console.log(" • Provider-specific files (instructions embedded)");
1603
- }
2108
+ const result = await syncAgentConfiguration6({ silent: false, adapters });
2109
+ const capList = result.capabilities.join(", ");
2110
+ console.log(`✓ Synced - ${capList}`);
1604
2111
  } catch (error) {
1605
2112
  console.error("");
1606
2113
  console.error("✗ Sync failed:");
@@ -1614,7 +2121,7 @@ import { debug } from "@omnidev-ai/core";
1614
2121
  var require2 = createRequire2(import.meta.url);
1615
2122
  function readCliVersion() {
1616
2123
  try {
1617
- const pkg = require2("../../package.json");
2124
+ const pkg = require2("../package.json");
1618
2125
  if (typeof pkg?.version === "string") {
1619
2126
  return pkg.version;
1620
2127
  }
@@ -1629,11 +2136,15 @@ async function buildDynamicApp() {
1629
2136
  add: addRoutes,
1630
2137
  capability: capabilityRoutes,
1631
2138
  profile: profileRoutes,
1632
- provider: providerRoutes
2139
+ provider: providerRoutes,
2140
+ security: securityRoutes
1633
2141
  };
1634
2142
  debug("Core routes registered", Object.keys(routes));
1635
- if (existsSync9(".omni/config.toml")) {
2143
+ const configPath = join9(process.cwd(), "omni.toml");
2144
+ debug("Checking for config", { configPath, exists: existsSync10(configPath), cwd: process.cwd() });
2145
+ if (existsSync10(configPath)) {
1636
2146
  try {
2147
+ debug("Loading capability commands...");
1637
2148
  const capabilityCommands = await loadCapabilityCommands();
1638
2149
  debug("Capability commands loaded", {
1639
2150
  commands: Object.keys(capabilityCommands),
@@ -1653,7 +2164,7 @@ async function buildDynamicApp() {
1653
2164
  }
1654
2165
  }
1655
2166
  debug("Final routes", Object.keys(routes));
1656
- const app = buildApplication(buildRouteMap5({
2167
+ const app = buildApplication(buildRouteMap6({
1657
2168
  routes,
1658
2169
  docs: {
1659
2170
  brief: "OmniDev commands"
@@ -1668,10 +2179,14 @@ async function buildDynamicApp() {
1668
2179
  return app;
1669
2180
  }
1670
2181
  async function loadCapabilityCommands() {
1671
- const { buildCapabilityRegistry, installCapabilityDependencies } = await import("@omnidev-ai/core");
2182
+ const { buildCapabilityRegistry: buildCapabilityRegistry2, installCapabilityDependencies } = await import("@omnidev-ai/core");
1672
2183
  await installCapabilityDependencies(true);
1673
- const registry = await buildCapabilityRegistry();
2184
+ const registry = await buildCapabilityRegistry2();
1674
2185
  const capabilities = registry.getAllCapabilities();
2186
+ debug("Registry built", {
2187
+ capabilityCount: capabilities.length,
2188
+ capabilities: capabilities.map((c) => ({ id: c.id, path: c.path }))
2189
+ });
1675
2190
  const commands = {};
1676
2191
  for (const capability of capabilities) {
1677
2192
  try {
@@ -1702,20 +2217,40 @@ async function loadCapabilityCommands() {
1702
2217
  }
1703
2218
  async function loadCapabilityExport(capability) {
1704
2219
  const capabilityPath = join9(process.cwd(), capability.path);
1705
- const indexPath = join9(capabilityPath, "index.ts");
1706
- if (!existsSync9(indexPath)) {
1707
- const jsIndexPath = join9(capabilityPath, "index.js");
1708
- if (!existsSync9(jsIndexPath)) {
1709
- return null;
1710
- }
1711
- const module2 = await import(jsIndexPath);
1712
- if (!module2.default) {
1713
- return null;
1714
- }
1715
- return module2.default;
2220
+ const builtIndexPath = join9(capabilityPath, "dist", "index.js");
2221
+ const jsIndexPath = join9(capabilityPath, "index.js");
2222
+ const tsIndexPath = join9(capabilityPath, "index.ts");
2223
+ debug(`Checking entry points for '${capability.id}'`, {
2224
+ capabilityPath,
2225
+ builtIndexPath,
2226
+ builtExists: existsSync10(builtIndexPath),
2227
+ jsIndexPath,
2228
+ jsExists: existsSync10(jsIndexPath),
2229
+ tsIndexPath,
2230
+ tsExists: existsSync10(tsIndexPath)
2231
+ });
2232
+ let indexPath = null;
2233
+ if (existsSync10(builtIndexPath)) {
2234
+ indexPath = builtIndexPath;
2235
+ } else if (existsSync10(jsIndexPath)) {
2236
+ indexPath = jsIndexPath;
2237
+ } else if (existsSync10(tsIndexPath)) {
2238
+ indexPath = tsIndexPath;
1716
2239
  }
2240
+ if (!indexPath) {
2241
+ debug(`No entry point found for '${capability.id}'`);
2242
+ return null;
2243
+ }
2244
+ debug(`Using entry point for '${capability.id}'`, { indexPath });
1717
2245
  const module = await import(indexPath);
2246
+ debug(`Module loaded for '${capability.id}'`, {
2247
+ hasDefault: !!module.default,
2248
+ moduleKeys: Object.keys(module),
2249
+ defaultType: typeof module.default,
2250
+ defaultKeys: module.default ? Object.keys(module.default) : []
2251
+ });
1718
2252
  if (!module.default) {
2253
+ debug(`No default export for '${capability.id}'`);
1719
2254
  return null;
1720
2255
  }
1721
2256
  const capExport = module.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnidev-ai/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,11 +28,11 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@inquirer/prompts": "^8.1.0",
31
- "@omnidev-ai/core": "0.11.0",
31
+ "@omnidev-ai/core": "0.12.0",
32
32
  "@stricli/core": "^1.2.5"
33
33
  },
34
34
  "devDependencies": {
35
- "@omnidev-ai/adapters": "0.0.11",
35
+ "@omnidev-ai/adapters": "0.1.1",
36
36
  "bunup": "^0.16.20"
37
37
  }
38
38
  }