securewithtrace 0.10.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 (3) hide show
  1. package/README.md +39 -2
  2. package/dist/index.js +353 -28
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  `tracecli` is the official CLI for Trace.
12
12
 
13
- Use it to authenticate, browse connected repositories, clone repositories, and inspect vulnerabilities directly from the command line.
13
+ Use it to authenticate, browse and filter connected repositories, clone repositories, and inspect vulnerabilities directly from the command line.
14
14
 
15
15
  ## Install
16
16
 
@@ -42,6 +42,9 @@ tracecli auth status
42
42
  # 3) List connected repositories
43
43
  tracecli repo list
44
44
 
45
+ # 3b) Filter repositories
46
+ tracecli repo list --platform GITHUB --status CONNECTED --query trace
47
+
45
48
  # 4) View vulnerabilities
46
49
  tracecli vulnerabilities list --severity HIGH
47
50
  ```
@@ -49,7 +52,8 @@ tracecli vulnerabilities list --severity HIGH
49
52
  ## What You Can Do
50
53
 
51
54
  - **Authenticate securely** with device authorization flow (`auth login/logout/status`)
52
- - **List repositories** connected to your Trace organization (`repo list`)
55
+ - **List and filter repositories** connected to your Trace organization (`repo list`, `repo filter`)
56
+ - **Find repositories by dependency** with optional version filtering (`repo list --dependency`)
53
57
  - **Clone repositories** with secure token handling (`repo clone`)
54
58
  - **Filter vulnerabilities** by repo, severity, and status (`vulnerabilities list`)
55
59
  - **Export results as JSON or TSV** for automation (`vulnerabilities list --output json`)
@@ -70,6 +74,27 @@ tracecli auth logout
70
74
  # List org repositories known to Trace
71
75
  tracecli repo list
72
76
 
77
+ # List only connected GitHub repositories that match "trace"
78
+ tracecli repo list --platform GITHUB --status CONNECTED --query trace
79
+
80
+ # Advanced filtering syntax (repeatable)
81
+ tracecli repo list --filter "platform=GITHUB" --filter "repo:trace" --filter "status!=DISCONNECTED"
82
+
83
+ # Alias command for filtering
84
+ tracecli repo filter --filter "language:typescript" --filter "branch=main"
85
+
86
+ # Find repositories that use a specific dependency
87
+ tracecli repo list --dependency lodash
88
+
89
+ # Filter by dependency at a specific version
90
+ tracecli repo list --dependency lodash@4.17.21
91
+
92
+ # Filter by scoped package with partial version match
93
+ tracecli repo list --dependency @types/node@20
94
+
95
+ # Dependency results as JSON
96
+ tracecli repo list --dependency react --json | jq '.[].fullName'
97
+
73
98
  # Clone by owner/repo shorthand
74
99
  tracecli repo clone securewithtrace/trace
75
100
 
@@ -114,6 +139,18 @@ Supported filter values:
114
139
  - `--severity`: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`
115
140
  - `--status`: `OPEN`, `IN_PROGRESS`, `FIXED`, `IGNORED`, `FALSE_POSITIVE`
116
141
 
142
+ Repository filter options:
143
+
144
+ - `--platform`: `GITHUB`, `GITLAB`, `BITBUCKET`
145
+ - `--status`: `CONNECTED`, `DISCONNECTED`
146
+ - `--query`: case-insensitive contains match across URL, repo name, default branch, and language
147
+ - `--default-branch`: case-insensitive contains match for default branch
148
+ - `--language`: case-insensitive contains match for primary language
149
+ - `--filter`: repeatable advanced expression in the form `<field><operator><value>`
150
+ - Supported fields: `platform`, `status`, `url`, `repo`, `branch`, `language`
151
+ - Supported operators: `=` (exact), `!=` (not exact), `:` (contains), `!:` (not contains)
152
+ - `--dependency`: filter by dependency name with optional `@version` (e.g. `lodash`, `lodash@4.17`, `@types/node@20`)
153
+
117
154
  ### Pentests
118
155
 
119
156
  ```bash
package/dist/index.js CHANGED
@@ -25736,6 +25736,9 @@ function extractRepoName(input) {
25736
25736
  return input;
25737
25737
  }
25738
25738
  var BRANCH_NAME_PATTERN = /^[a-zA-Z0-9._/+-]+$/;
25739
+ var TRACE_GIT_ASKPASS_TOKEN_ENV = "TRACE_GIT_ASKPASS_TOKEN";
25740
+ var REPOSITORY_PLATFORMS = ["GITHUB", "GITLAB", "BITBUCKET"];
25741
+ var REPOSITORY_STATUSES = ["CONNECTED", "DISCONNECTED"];
25739
25742
  function validateBranchName(branch) {
25740
25743
  if (branch.startsWith("-")) {
25741
25744
  throw new Error(`Invalid branch name: '${branch}' \u2014 branch names cannot start with a dash`);
@@ -25745,29 +25748,341 @@ function validateBranchName(branch) {
25745
25748
  }
25746
25749
  return branch;
25747
25750
  }
25748
- function createListCommand2() {
25749
- return new Command("list").description("List repositories in your organization").action(async () => {
25750
- const organizationId = await resolveOrganizationId();
25751
- const result = await authenticatedRequest(
25752
- (client) => client.repository.listRepositories({
25753
- params: { organizationId }
25754
- })
25751
+ function parseDependencyFilter(filter) {
25752
+ const isScoped = filter.startsWith("@");
25753
+ const separatorIndex = isScoped ? filter.indexOf("@", 1) : filter.indexOf("@");
25754
+ if (separatorIndex === -1) {
25755
+ return { name: filter };
25756
+ }
25757
+ const name = filter.slice(0, separatorIndex);
25758
+ const version2 = filter.slice(separatorIndex + 1);
25759
+ if (!name || !version2) {
25760
+ return { name: filter };
25761
+ }
25762
+ return { name, version: version2 };
25763
+ }
25764
+ function parseCloneDepth(value) {
25765
+ const depth = Number.parseInt(value, 10);
25766
+ if (!Number.isInteger(depth) || depth < 1) {
25767
+ throw new Error("--depth must be a positive integer");
25768
+ }
25769
+ return depth;
25770
+ }
25771
+ function normalizeForMatching(value) {
25772
+ return value.trim().toLowerCase();
25773
+ }
25774
+ function normalizePlatform(value, source) {
25775
+ const normalized = value.trim().toUpperCase();
25776
+ if (REPOSITORY_PLATFORMS.includes(normalized)) {
25777
+ return normalized;
25778
+ }
25779
+ throw new Error(
25780
+ `${source} must be one of: ${REPOSITORY_PLATFORMS.join(", ")}. Received '${value}'.`
25781
+ );
25782
+ }
25783
+ function normalizeStatus(value, source) {
25784
+ const normalized = value.trim().toUpperCase();
25785
+ if (REPOSITORY_STATUSES.includes(normalized)) {
25786
+ return normalized;
25787
+ }
25788
+ throw new Error(
25789
+ `${source} must be one of: ${REPOSITORY_STATUSES.join(", ")}. Received '${value}'.`
25790
+ );
25791
+ }
25792
+ function normalizeFilterField(rawField) {
25793
+ const field = rawField.trim().toLowerCase();
25794
+ switch (field) {
25795
+ case "platform":
25796
+ return "platform";
25797
+ case "status":
25798
+ return "status";
25799
+ case "url":
25800
+ case "repository":
25801
+ case "repositoryurl":
25802
+ case "repository_url":
25803
+ return "repository_url";
25804
+ case "repo":
25805
+ case "name":
25806
+ case "repository_name":
25807
+ return "repo_name";
25808
+ case "branch":
25809
+ case "defaultbranch":
25810
+ case "default_branch":
25811
+ return "default_branch";
25812
+ case "language":
25813
+ case "primarylanguage":
25814
+ case "primary_language":
25815
+ return "language";
25816
+ default:
25817
+ throw new Error(
25818
+ `Unknown repository filter field '${rawField}'. Supported fields: platform, status, url, repo, branch, language.`
25819
+ );
25820
+ }
25821
+ }
25822
+ function parseFilterOperator(rawOperator) {
25823
+ switch (rawOperator) {
25824
+ case "=":
25825
+ return "exact";
25826
+ case "!=":
25827
+ return "not_exact";
25828
+ case ":":
25829
+ return "contains";
25830
+ case "!:":
25831
+ return "not_contains";
25832
+ default:
25833
+ throw new Error(`Unsupported filter operator '${rawOperator}'. Use one of: =, !=, :, !:.`);
25834
+ }
25835
+ }
25836
+ function parseRepositoryFilterExpression(expression) {
25837
+ const trimmed = expression.trim();
25838
+ const match = /^([a-zA-Z][a-zA-Z0-9_-]*)(!=|!:|=|:)(.+)$/.exec(trimmed);
25839
+ if (!match) {
25840
+ throw new Error(
25841
+ `Invalid filter '${expression}'. Expected format: <field><operator><value> (e.g., platform=GITHUB, repo:trace, status!=DISCONNECTED).`
25755
25842
  );
25756
- if (result.status !== 200) {
25757
- console.error(`Failed to list repositories (status ${result.status}).`);
25758
- process.exit(1);
25843
+ }
25844
+ const rawField = match[1];
25845
+ const rawOperator = match[2];
25846
+ const rawValue = match[3];
25847
+ if (!rawField || !rawOperator || !rawValue) {
25848
+ throw new Error(`Invalid filter '${expression}'.`);
25849
+ }
25850
+ const value = rawValue.trim();
25851
+ if (!value) {
25852
+ throw new Error(`Invalid filter '${expression}': value cannot be empty.`);
25853
+ }
25854
+ const field = normalizeFilterField(rawField);
25855
+ const operator = parseFilterOperator(rawOperator);
25856
+ if (field === "platform") {
25857
+ normalizePlatform(value, "--filter");
25858
+ }
25859
+ if (field === "status") {
25860
+ normalizeStatus(value, "--filter");
25861
+ }
25862
+ return { field, operator, value };
25863
+ }
25864
+ function doesValueMatchFilter(candidate, filterValue, operator, exactMatchOnly) {
25865
+ const normalizedCandidate = normalizeForMatching(candidate);
25866
+ const normalizedFilter = normalizeForMatching(filterValue);
25867
+ if (exactMatchOnly || operator === "exact" || operator === "not_exact") {
25868
+ const matches2 = normalizedCandidate === normalizedFilter;
25869
+ return operator === "not_exact" ? !matches2 : matches2;
25870
+ }
25871
+ const matches = normalizedCandidate.includes(normalizedFilter);
25872
+ return operator === "not_contains" ? !matches : matches;
25873
+ }
25874
+ function matchParsedFilter(repo, parsedFilter) {
25875
+ switch (parsedFilter.field) {
25876
+ case "platform":
25877
+ return doesValueMatchFilter(repo.platform, parsedFilter.value, parsedFilter.operator, true);
25878
+ case "status":
25879
+ return doesValueMatchFilter(repo.status, parsedFilter.value, parsedFilter.operator, true);
25880
+ case "repository_url":
25881
+ return doesValueMatchFilter(
25882
+ repo.repositoryUrl,
25883
+ parsedFilter.value,
25884
+ parsedFilter.operator,
25885
+ false
25886
+ );
25887
+ case "repo_name":
25888
+ return doesValueMatchFilter(
25889
+ extractRepoName(repo.repositoryUrl),
25890
+ parsedFilter.value,
25891
+ parsedFilter.operator,
25892
+ false
25893
+ );
25894
+ case "default_branch":
25895
+ return doesValueMatchFilter(
25896
+ repo.defaultBranch,
25897
+ parsedFilter.value,
25898
+ parsedFilter.operator,
25899
+ false
25900
+ );
25901
+ case "language": {
25902
+ const language = repo.primaryLanguage ?? "";
25903
+ return doesValueMatchFilter(language, parsedFilter.value, parsedFilter.operator, false);
25904
+ }
25905
+ }
25906
+ }
25907
+ function filterRepositories(repositories, options) {
25908
+ const parsedFilters = (options.filters ?? []).map(
25909
+ (expression) => parseRepositoryFilterExpression(expression)
25910
+ );
25911
+ const normalizedPlatform = options.platform ? normalizePlatform(options.platform, "--platform") : void 0;
25912
+ const normalizedStatus = options.status ? normalizeStatus(options.status, "--status") : void 0;
25913
+ const normalizedQuery = options.query ? normalizeForMatching(options.query) : void 0;
25914
+ const normalizedDefaultBranch = options.defaultBranch ? normalizeForMatching(options.defaultBranch) : void 0;
25915
+ const normalizedLanguage = options.language ? normalizeForMatching(options.language) : void 0;
25916
+ return repositories.filter((repo) => {
25917
+ if (normalizedPlatform && repo.platform.toUpperCase() !== normalizedPlatform) {
25918
+ return false;
25919
+ }
25920
+ if (normalizedStatus && repo.status.toUpperCase() !== normalizedStatus) {
25921
+ return false;
25922
+ }
25923
+ if (normalizedDefaultBranch && !normalizeForMatching(repo.defaultBranch).includes(normalizedDefaultBranch)) {
25924
+ return false;
25925
+ }
25926
+ const repoLanguage = normalizeForMatching(repo.primaryLanguage ?? "");
25927
+ if (normalizedLanguage && !repoLanguage.includes(normalizedLanguage)) {
25928
+ return false;
25929
+ }
25930
+ if (normalizedQuery) {
25931
+ const normalizedUrl = normalizeForMatching(repo.repositoryUrl);
25932
+ const normalizedBranch = normalizeForMatching(repo.defaultBranch);
25933
+ const normalizedName = normalizeForMatching(extractRepoName(repo.repositoryUrl));
25934
+ if (!normalizedUrl.includes(normalizedQuery) && !normalizedBranch.includes(normalizedQuery) && !repoLanguage.includes(normalizedQuery) && !normalizedName.includes(normalizedQuery)) {
25935
+ return false;
25936
+ }
25937
+ }
25938
+ for (const parsedFilter of parsedFilters) {
25939
+ if (!matchParsedFilter(repo, parsedFilter)) {
25940
+ return false;
25941
+ }
25759
25942
  }
25760
- const repos = result.body;
25761
- if (repos.length === 0) {
25762
- console.log("No repositories found.");
25943
+ return true;
25944
+ });
25945
+ }
25946
+ function collectFilterExpression(value, previous) {
25947
+ previous.push(value);
25948
+ return previous;
25949
+ }
25950
+ function toRepositoryListFilterOptions(options) {
25951
+ return {
25952
+ ...options.platform ? { platform: options.platform } : {},
25953
+ ...options.status ? { status: options.status } : {},
25954
+ ...options.query ? { query: options.query } : {},
25955
+ ...options.defaultBranch ? { defaultBranch: options.defaultBranch } : {},
25956
+ ...options.language ? { language: options.language } : {},
25957
+ ...options.filter.length > 0 ? { filters: options.filter } : {}
25958
+ };
25959
+ }
25960
+ function addRepositoryFilterOptions(command) {
25961
+ return command.option(
25962
+ "--platform <platform>",
25963
+ "Filter by platform (GITHUB, GITLAB, BITBUCKET)",
25964
+ (value) => value.toUpperCase()
25965
+ ).option(
25966
+ "--status <status>",
25967
+ "Filter by status (CONNECTED, DISCONNECTED)",
25968
+ (value) => value.toUpperCase()
25969
+ ).option("--query <query>", "Case-insensitive search across URL, repo, branch, and language").option("--default-branch <branch>", "Filter by default branch (case-insensitive contains)").option("--language <language>", "Filter by primary language (case-insensitive contains)").option(
25970
+ "--filter <expression>",
25971
+ "Advanced filter expression. Repeatable. Examples: platform=GITHUB, repo:trace, status!=DISCONNECTED, language!:typescript",
25972
+ collectFilterExpression,
25973
+ []
25974
+ ).option("--json", "Output as JSON").option(
25975
+ "--dependency <pkg>",
25976
+ "Filter repositories that use a dependency (e.g. lodash, lodash@4.17.21, @types/node@20)"
25977
+ );
25978
+ }
25979
+ async function runRepositoryListCommand(options) {
25980
+ const organizationId = await resolveOrganizationId();
25981
+ if (options.dependency) {
25982
+ await runDependencyFilter(organizationId, options);
25983
+ return;
25984
+ }
25985
+ let filteredRepositories;
25986
+ const result = await authenticatedRequest(
25987
+ (client) => client.repository.listRepositories({
25988
+ params: { organizationId }
25989
+ })
25990
+ );
25991
+ if (result.status !== 200) {
25992
+ console.error(`Failed to list repositories (status ${result.status}).`);
25993
+ process.exit(1);
25994
+ }
25995
+ try {
25996
+ filteredRepositories = filterRepositories(result.body, toRepositoryListFilterOptions(options));
25997
+ } catch (error48) {
25998
+ const message = error48 instanceof Error ? error48.message : "Unknown filter parsing error.";
25999
+ console.error(`Invalid repository filter: ${message}`);
26000
+ process.exit(1);
26001
+ }
26002
+ if (filteredRepositories.length === 0) {
26003
+ if (options.platform || options.status || options.query || options.defaultBranch || options.language || options.filter.length > 0) {
26004
+ console.log("No repositories found matching the provided filters.");
25763
26005
  return;
25764
26006
  }
25765
- for (const repo of repos) {
25766
- const status = repo.status === "CONNECTED" ? "connected" : "disconnected";
25767
- const url2 = stripAnsi(String(repo.repositoryUrl));
25768
- const platform = stripAnsi(String(repo.platform)).toLowerCase();
25769
- console.log(`${url2} (${platform}, ${status})`);
26007
+ console.log("No repositories found.");
26008
+ return;
26009
+ }
26010
+ if (options.json) {
26011
+ console.log(JSON.stringify(filteredRepositories, null, 2));
26012
+ return;
26013
+ }
26014
+ for (const repo of filteredRepositories) {
26015
+ const status = repo.status === "CONNECTED" ? "connected" : "disconnected";
26016
+ const url2 = stripAnsi(String(repo.repositoryUrl));
26017
+ const platform = stripAnsi(String(repo.platform)).toLowerCase();
26018
+ console.log(`${url2} (${platform}, ${status})`);
26019
+ }
26020
+ }
26021
+ async function runDependencyFilter(organizationId, options) {
26022
+ const { name, version: version2 } = parseDependencyFilter(options.dependency);
26023
+ const result = await authenticatedRequest(
26024
+ (client) => client.dependency.searchDependencies({
26025
+ params: { organizationId },
26026
+ body: {
26027
+ query: name,
26028
+ ...version2 ? { versionPrefix: version2 } : {},
26029
+ pageSize: 100
26030
+ }
26031
+ })
26032
+ );
26033
+ if (result.status !== 200) {
26034
+ console.error(`Failed to search dependencies (status ${result.status}).`);
26035
+ process.exit(1);
26036
+ }
26037
+ const repoMap = /* @__PURE__ */ new Map();
26038
+ for (const pkg of result.body.packages) {
26039
+ if (pkg.name.toLowerCase() !== name.toLowerCase()) {
26040
+ continue;
26041
+ }
26042
+ for (const repo of pkg.repositories) {
26043
+ const existing = repoMap.get(repo.repositoryId);
26044
+ const versionStrings = repo.versions.map((v) => v.version);
26045
+ if (existing) {
26046
+ for (const v of versionStrings) {
26047
+ if (!existing.versions.includes(v)) {
26048
+ existing.versions.push(v);
26049
+ }
26050
+ }
26051
+ } else {
26052
+ repoMap.set(repo.repositoryId, {
26053
+ fullName: repo.fullName,
26054
+ versions: versionStrings
26055
+ });
26056
+ }
25770
26057
  }
26058
+ }
26059
+ const repos = Array.from(repoMap.values());
26060
+ if (repos.length === 0) {
26061
+ const label = version2 ? `${name}@${version2}` : name;
26062
+ console.log(`No repositories found using dependency '${label}'.`);
26063
+ return;
26064
+ }
26065
+ if (options.json) {
26066
+ console.log(JSON.stringify(repos, null, 2));
26067
+ return;
26068
+ }
26069
+ for (const repo of repos) {
26070
+ const versions = repo.versions.join(", ");
26071
+ console.log(`${repo.fullName} (${versions})`);
26072
+ }
26073
+ }
26074
+ function createListCommand2() {
26075
+ return addRepositoryFilterOptions(
26076
+ new Command("list").description("List repositories in your organization")
26077
+ ).action(async (options) => {
26078
+ await runRepositoryListCommand(options);
26079
+ });
26080
+ }
26081
+ function createFilterCommand() {
26082
+ return addRepositoryFilterOptions(
26083
+ new Command("filter").description("Filter repositories in your organization")
26084
+ ).action(async (options) => {
26085
+ await runRepositoryListCommand(options);
25771
26086
  });
25772
26087
  }
25773
26088
  function urlWithUsername(httpsUrl) {
@@ -25776,14 +26091,21 @@ function urlWithUsername(httpsUrl) {
25776
26091
  url2.password = "";
25777
26092
  return url2.toString();
25778
26093
  }
25779
- function createAskPassScript(token) {
26094
+ function createAskPassScript() {
25780
26095
  const dir = mkdtempSync(join3(tmpdir(), "trace-askpass-"));
25781
26096
  const scriptPath = join3(dir, "askpass.sh");
25782
- writeFileSync3(scriptPath, `#!/bin/sh
25783
- echo '${token.replace(/'/g, "'\\''")}'
25784
- `, {
25785
- mode: 448
25786
- });
26097
+ writeFileSync3(
26098
+ scriptPath,
26099
+ `#!/bin/sh
26100
+ if [ -z "\${${TRACE_GIT_ASKPASS_TOKEN_ENV}:-}" ]; then
26101
+ exit 1
26102
+ fi
26103
+ printf '%s\\n' "$${TRACE_GIT_ASKPASS_TOKEN_ENV}"
26104
+ `,
26105
+ {
26106
+ mode: 448
26107
+ }
26108
+ );
25787
26109
  chmodSync(scriptPath, 448);
25788
26110
  return scriptPath;
25789
26111
  }
@@ -25803,7 +26125,8 @@ async function fetchCloneToken(repoUrl) {
25803
26125
  return null;
25804
26126
  }
25805
26127
  if (result.status === 200) {
25806
- return result.body.token;
26128
+ const token = result.body.token.trim();
26129
+ return token.length > 0 ? token : null;
25807
26130
  }
25808
26131
  return null;
25809
26132
  } catch {
@@ -25811,7 +26134,7 @@ async function fetchCloneToken(repoUrl) {
25811
26134
  }
25812
26135
  }
25813
26136
  function createCloneCommand() {
25814
- return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", Number.parseInt).action(
26137
+ return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", parseCloneDepth).action(
25815
26138
  async (repository, directory, options) => {
25816
26139
  const cloneUrl = resolveRepoUrl(repository);
25817
26140
  const targetDir = directory ?? extractRepoName(repository);
@@ -25833,8 +26156,9 @@ function createCloneCommand() {
25833
26156
  console.log(`Cloning ${cloneUrl} into ${targetDir}...`);
25834
26157
  const extraEnv = {};
25835
26158
  if (installationToken) {
25836
- askPassScript = createAskPassScript(installationToken);
26159
+ askPassScript = createAskPassScript();
25837
26160
  extraEnv["GIT_ASKPASS"] = askPassScript;
26161
+ extraEnv[TRACE_GIT_ASKPASS_TOKEN_ENV] = installationToken;
25838
26162
  extraEnv["GIT_TERMINAL_PROMPT"] = "0";
25839
26163
  }
25840
26164
  try {
@@ -25845,7 +26169,7 @@ function createCloneCommand() {
25845
26169
  });
25846
26170
  const exitCode = await proc.exited;
25847
26171
  if (exitCode !== 0) {
25848
- process.exit(exitCode);
26172
+ process.exit(1);
25849
26173
  }
25850
26174
  if (installationToken) {
25851
26175
  const resetProc = Bun.spawn(["git", "remote", "set-url", "origin", cloneUrl], {
@@ -25872,6 +26196,7 @@ function createCloneCommand() {
25872
26196
  function createRepoCommand() {
25873
26197
  const repo = new Command("repo").description("Manage repositories");
25874
26198
  repo.addCommand(createListCommand2());
26199
+ repo.addCommand(createFilterCommand());
25875
26200
  repo.addCommand(createCloneCommand());
25876
26201
  return repo;
25877
26202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {