crowbar-cli 0.1.0__tar.gz → 0.1.1__tar.gz

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 (45) hide show
  1. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/PKG-INFO +1 -1
  2. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/__init__.py +1 -1
  3. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/LAUNCH.md +1 -1
  4. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/install.mjs +25 -12
  5. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/.gitignore +0 -0
  6. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/AGENTS.md +0 -0
  7. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/LICENSE +0 -0
  8. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/Makefile +0 -0
  9. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/README.md +0 -0
  10. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/bin/crowbar +0 -0
  11. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/catalog.py +0 -0
  12. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/cli.py +0 -0
  13. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/datasets.py +0 -0
  14. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/db.py +0 -0
  15. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/indexer.py +0 -0
  16. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/intel.py +0 -0
  17. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/loaders.py +0 -0
  18. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/mcp.py +0 -0
  19. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/paths.py +0 -0
  20. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/crowbar/query.py +0 -0
  21. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/DATASETS.md +0 -0
  22. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/INTEL.md +0 -0
  23. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/INTEL_RESEARCH_PROMPT.md +0 -0
  24. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/MAP.md +0 -0
  25. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/NIX.md +0 -0
  26. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/assets/agent-session.gif +0 -0
  27. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/assets/demo.gif +0 -0
  28. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/docs/assets/flow.svg +0 -0
  29. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/mcp/server.py +0 -0
  30. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/pyproject.toml +0 -0
  31. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/age_index.py +0 -0
  32. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/check.py +0 -0
  33. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/fetch_intel.py +0 -0
  34. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/install.test.mjs +0 -0
  35. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/make_fixture.py +0 -0
  36. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/merge_p0.py +0 -0
  37. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/rollback_index.sh +0 -0
  38. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/selftest.py +0 -0
  39. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/scripts/smoke.py +0 -0
  40. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/skills/crowbar/SKILL.md +0 -0
  41. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/tests/__init__.py +0 -0
  42. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/tests/support.py +0 -0
  43. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/tests/test_corpus.py +0 -0
  44. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/tests/test_unit.py +0 -0
  45. {crowbar_cli-0.1.0 → crowbar_cli-0.1.1}/tests/test_v3.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: crowbar-cli
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Retrieve CVEs and index them inside your codebase. One SQLite+FTS5 index, one CLI, one MCP server.
5
5
  Project-URL: Homepage, https://github.com/kushvinth/crowbar
6
6
  Project-URL: Repository, https://github.com/kushvinth/crowbar
@@ -1,3 +1,3 @@
1
1
  """crowbar - local vulnerability intelligence, terminal-first."""
2
2
 
3
- __version__ = "0.1.0" # x-release-please-version
3
+ __version__ = "0.1.1" # x-release-please-version
@@ -27,7 +27,7 @@ to `main`, `release-please` opens/updates a release PR; merging it tags
27
27
 
28
28
  So after the one-time setup, **publishing = merge the release PR**.
29
29
 
30
- The first cut is pinned: `release-as: 0.1.0` in `release-please-config.json` makes the release PR propose 0.1.0 whatever the commits since say. **Delete that line right after v0.1.0 is tagged**, or every later release PR proposes 0.1.0 again.
30
+ v0.1.0 was cut with a temporary `release-as: 0.1.0` pin in `release-please-config.json`, removed once tagged; versions now follow the Conventional Commits since the last release.
31
31
 
32
32
  Registries publish only once switched on: set the repo variables `PUBLISH_NPM` / `PUBLISH_PYPI` to `true` (Settings > Secrets and variables > Actions > Variables). npm needs `NPM_TOKEN` to be a granular token with "bypass 2FA" enabled; PyPI needs the trusted publisher (owner `kushvinth`, repo `crowbar`, workflow `release.yml`, environment `pypi`). To publish an existing release to a registry switched on later, run the `release` workflow by hand with that tag (e.g. `gh workflow run release -f tag=v0.1.0`); versions already on a registry are skipped.
33
33
 
@@ -81,10 +81,21 @@ export function describe(step, remove = false) {
81
81
  }
82
82
  }
83
83
 
84
- function backup(file, stamp) {
85
- if (!fs.existsSync(file)) return null;
84
+ // One read per file: the backup and the edit both start from this text, so
85
+ // nothing can change the file between a check and its use.
86
+ function readText(file) {
87
+ try {
88
+ return fs.readFileSync(file, "utf8");
89
+ } catch (e) {
90
+ if (e.code === "ENOENT") return null;
91
+ throw e;
92
+ }
93
+ }
94
+
95
+ function backup(file, text, stamp) {
96
+ if (text === null) return null;
86
97
  const dst = `${file}.crowbar-backup-${stamp}`;
87
- fs.copyFileSync(file, dst);
98
+ fs.writeFileSync(dst, text);
88
99
  return dst;
89
100
  }
90
101
 
@@ -104,11 +115,12 @@ export function apply(step, stamp = Date.now()) {
104
115
  return "registered";
105
116
  }
106
117
  fs.mkdirSync(path.dirname(step.file), { recursive: true });
107
- const text = fs.existsSync(step.file) ? fs.readFileSync(step.file, "utf8") : "";
118
+ const original = readText(step.file);
119
+ const text = original ?? "";
108
120
  if (step.kind === "toml") {
109
121
  const header = `[${step.table}]`;
110
122
  if (text.split(/\r?\n/).some((l) => l.trim() === header)) return "already registered";
111
- const saved = backup(step.file, stamp);
123
+ const saved = backup(step.file, original, stamp);
112
124
  const sep = text && !text.endsWith("\n") ? "\n\n" : text ? "\n" : "";
113
125
  fs.writeFileSync(step.file, `${text}${sep}${header}\n${step.body}`);
114
126
  return saved ? `updated (backup ${path.basename(saved)})` : "created";
@@ -120,7 +132,7 @@ export function apply(step, stamp = Date.now()) {
120
132
  // JSONC with comments or a broken file: leave it alone rather than rewrite it
121
133
  throw new Error(`${step.file} is not plain JSON - add crowbar by hand (npx crowbar-cli prints the block)`);
122
134
  }
123
- const saved = backup(step.file, stamp);
135
+ const saved = backup(step.file, original, stamp);
124
136
  step.mutate(cfg);
125
137
  fs.writeFileSync(step.file, JSON.stringify(cfg, null, 2) + "\n");
126
138
  return saved ? `updated (backup ${path.basename(saved)})` : "created";
@@ -141,8 +153,8 @@ export function unapply(step, stamp = Date.now()) {
141
153
  if (r.status !== 0) throw new Error((r.stderr || r.stdout || "failed").trim());
142
154
  return "unregistered";
143
155
  }
144
- if (!fs.existsSync(step.file)) return "not registered";
145
- const text = fs.readFileSync(step.file, "utf8");
156
+ const text = readText(step.file);
157
+ if (text === null) return "not registered";
146
158
  if (step.kind === "toml") {
147
159
  const lines = text.split(/\r?\n/);
148
160
  const start = lines.findIndex((l) => l.trim() === `[${step.table}]`);
@@ -151,7 +163,7 @@ export function unapply(step, stamp = Date.now()) {
151
163
  while (end < lines.length && !(/^\s*\[/.test(lines[end]) && !lines[end].trim().startsWith(`[${step.table}.`))) end++;
152
164
  let from = start; // and takes the blank lines setup put before it
153
165
  while (from > 0 && !lines[from - 1].trim()) from--;
154
- const saved = backup(step.file, stamp);
166
+ const saved = backup(step.file, text, stamp);
155
167
  const rest = [...lines.slice(0, from), ...(from && end < lines.length ? [""] : []), ...lines.slice(end)];
156
168
  fs.writeFileSync(step.file, rest.join("\n").replace(/\n*$/, rest.some((l) => l.trim()) ? "\n" : ""));
157
169
  return `removed (backup ${path.basename(saved)})`;
@@ -162,9 +174,10 @@ export function unapply(step, stamp = Date.now()) {
162
174
  } catch {
163
175
  throw new Error(`${step.file} is not plain JSON - remove crowbar by hand`);
164
176
  }
165
- if (!cfg[step.key]?.crowbar) return "not registered";
166
- const saved = backup(step.file, stamp);
167
- delete cfg[step.key].crowbar;
177
+ const servers = Object.hasOwn(cfg, step.key) ? cfg[step.key] : null;
178
+ if (!servers || typeof servers !== "object" || !Object.hasOwn(servers, "crowbar")) return "not registered";
179
+ const saved = backup(step.file, text, stamp);
180
+ delete servers.crowbar;
168
181
  fs.writeFileSync(step.file, JSON.stringify(cfg, null, 2) + "\n");
169
182
  return `removed (backup ${path.basename(saved)})`;
170
183
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes