sharekit-cli 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0cde6c59b8fa8f0a9b7405d8f67ccba50390a1cfeb03a10628f39cfc7b1c823c
4
+ data.tar.gz: 9d86a7780e71640e1597d57912113a9c154256eb799aa2e28e7408686dbb2844
5
+ SHA512:
6
+ metadata.gz: 7bea1ae6dc74aab3e5a3a57d45e4c9ad986b046485f26020a2bbafbb1cb626250e30a748490b671cd6be9c78be62d729cdade72df9e6afcacb04496ca24bf8c5
7
+ data.tar.gz: 8a02b6895b8318687478166602ec1341cc537b2d44e4b3351ab6771807c2df9679a07b380d99557c11caf50f9a9267221716e1db0823c6111e951df1f064585b
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.10
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-07-25
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Lucas Santana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # sharekit-cli
2
+
3
+ A small CLI that scaffolds a publishable "AI coding setup" profile and scans a directory for
4
+ leaked secrets — private keys, cloud/API tokens, sensitive env vars — blocking on high-severity
5
+ findings unless you pass `--force`.
6
+
7
+ It's a Ruby port of the `init` and `scan` commands from
8
+ [sharekit](https://github.com/LucasSantana-Dev/sharekit), a TypeScript CLI I built and publish
9
+ to npm. This port is deliberately not a line-for-line translation — it's rebuilt around Ruby
10
+ idioms that don't have a clean TS equivalent:
11
+
12
+ - **`Data.define`** for the immutable `Finding` value object, with `Comparable` mixed in for
13
+ free severity-ranked sorting, and free `Hash` deconstruction for pattern matching.
14
+ - **A rule table, not an if/elsif chain.** Each secret pattern is a `Scanner::Rule` (a `Data`
15
+ object: pattern + severity + preview metadata), stored in a frozen array. Adding a rule means
16
+ appending to `RULES` — the scan loop itself never changes.
17
+ - **`Enumerator` dual API.** `Scanner.scan` yields to a block when one is given, or returns an
18
+ `Enumerator` when it isn't (`return to_enum(:scan, ...) unless block_given?`) — the same
19
+ pattern as `Array#each`. Callers can stream (`each { }`) or collect (`.to_a`, `.first`, `.lazy`)
20
+ without the scanner caring which.
21
+ - **`case/in` pattern matching** in `Reporter.format_line`, matching directly on the `Finding`'s
22
+ deconstructed fields instead of manual attribute access.
23
+ - **Thor** for CLI dispatch instead of hand-parsed `ARGV`/flags.
24
+ - **`git ls-files`, not a hand-rolled ignore-file parser.** `scan` defers to git for
25
+ `.gitignore`-aware file listing when run inside a repo (via `Open3`, argv-array — no shell
26
+ interpolation), instead of reimplementing gitignore match semantics.
27
+
28
+ ## Install
29
+
30
+ Not yet published to RubyGems. Install straight from git:
31
+
32
+ ```bash
33
+ gem install specific_install
34
+ gem specific_install https://github.com/LucasSantana-Dev/sharekit-cli.git
35
+ ```
36
+
37
+ Or add to a `Gemfile`:
38
+
39
+ ```ruby
40
+ gem "sharekit-cli", git: "https://github.com/LucasSantana-Dev/sharekit-cli.git"
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```bash
46
+ sharekit-cli scan [DIR] # scan a directory (default: .)
47
+ sharekit-cli scan [DIR] --force # don't block on high-severity findings
48
+
49
+ sharekit-cli init [SKILL...] # scaffold ./sharekit-profile
50
+ sharekit-cli init --dir PATH [SKILL...] # scaffold at a custom path
51
+ sharekit-cli init --force # overwrite an existing dir; override secret blocking
52
+ ```
53
+
54
+ ```
55
+ $ sharekit-cli scan .
56
+
57
+ ⚠ Secret patterns detected:
58
+ .env:1 [AWS Access Key ID] …Y=AKIAEXAMPLEKEY000000
59
+
60
+ ⚠ Review and redact secrets before pushing to a public repository.
61
+
62
+ Secrets export blocked: 1 high-severity finding(s) detected. Review and remove secrets, or re-run with --force to override.
63
+ ```
64
+
65
+ Both commands exit `1` when a high-severity secret is found and `--force` wasn't passed; `0`
66
+ otherwise. `init` copies `~/.claude/CLAUDE.md` and `~/.cursor/.cursorrules` into the new profile
67
+ (or writes placeholders if they don't exist) and scans everything it copies for secrets before
68
+ reporting success.
69
+
70
+ ### Rules
71
+
72
+ | Rule | Severity |
73
+ |--------------------------------|----------|
74
+ | Private Key Block | high |
75
+ | AWS Access Key ID | high |
76
+ | GitHub Personal Access Token | high |
77
+ | Slack Token | high |
78
+ | Google API Key | high |
79
+ | Bearer Token (JWT) | high |
80
+ | Home Directory Path Leak | low |
81
+ | Env Var: Sensitive Key | medium |
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ bin/setup # install dependencies
87
+ bundle exec rspec # run the test suite (61 examples)
88
+ bundle exec rubocop # lint
89
+ bundle exec bundler-audit check --update # dependency vulnerability scan
90
+ bin/console # interactive prompt
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/exe/sharekit-cli ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "sharekit/cli"
5
+
6
+ Sharekit::Cli::App.start(ARGV)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Sharekit
6
+ module Cli
7
+ # Picks which files a scan should look at. Inside a git repo, defers to
8
+ # `git ls-files` (tracked + untracked-but-not-ignored) so .gitignore is
9
+ # respected for free instead of reimplementing its match semantics.
10
+ # Outside a git repo, falls back to a plain glob with noise directories
11
+ # excluded.
12
+ module FileLister
13
+ DEFAULT_EXCLUDED_DIRS = %w[.git node_modules vendor .bundle].freeze
14
+
15
+ module_function
16
+
17
+ def list(dir)
18
+ git_tracked_files(dir) || glob_files(dir)
19
+ end
20
+
21
+ # Returns nil (not an empty array) when +dir+ isn't inside a git repo or
22
+ # git isn't installed, so the caller falls back to glob_files instead of
23
+ # silently scanning nothing.
24
+ def git_tracked_files(dir)
25
+ out, status = Open3.capture2("git", "-C", dir, "ls-files", "--cached", "--others", "--exclude-standard")
26
+ return nil unless status.success?
27
+
28
+ out.lines.map { |line| File.join(dir, line.chomp) }
29
+ rescue Errno::ENOENT
30
+ nil
31
+ end
32
+
33
+ def glob_files(dir)
34
+ Dir.glob("#{dir}/**/*").select { |path| File.file?(path) && !excluded?(path) }
35
+ end
36
+
37
+ def excluded?(path)
38
+ DEFAULT_EXCLUDED_DIRS.any? { |dir_name| path.split(File::SEPARATOR).include?(dir_name) }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharekit
4
+ module Cli
5
+ SEVERITY_RANK = { high: 2, medium: 1, low: 0 }.freeze
6
+
7
+ # Immutable value object for one scan hit. Data.define gives us structural
8
+ # equality and Hash-pattern deconstruction (`in {severity:, rule:}`) for free —
9
+ # a plain TS interface has neither.
10
+ Finding = Data.define(:rule, :file, :line, :preview, :severity) do
11
+ include Comparable
12
+
13
+ def <=>(other)
14
+ SEVERITY_RANK.fetch(severity) <=> SEVERITY_RANK.fetch(other.severity)
15
+ end
16
+
17
+ def high? = severity == :high
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Sharekit
6
+ module Cli
7
+ # Scaffolds a publishable sharekit profile: sharekit.toml, a claude/
8
+ # CLAUDE.md, a cursor/.cursorrules, an empty shared/, and any requested
9
+ # skill directories — copied from the caller's own AI-tooling config, so
10
+ # the result becomes an install target for `sharekit install <user>`.
11
+ class Init
12
+ Result = Data.define(:profile_dir, :findings, :created)
13
+ SourceFile = Data.define(:source, :dest, :placeholder)
14
+
15
+ def self.call(profile_dir:, skill_names: [], source_root: ENV.fetch("HOME", Dir.pwd), force: false)
16
+ new(profile_dir:, skill_names:, source_root:, force:).call
17
+ end
18
+
19
+ def initialize(profile_dir:, skill_names:, source_root:, force:)
20
+ @profile_dir = profile_dir
21
+ @skill_names = skill_names
22
+ @source_root = source_root
23
+ @force = force
24
+ @findings = []
25
+ @created = []
26
+ end
27
+
28
+ STANDARD_FILES = [
29
+ SourceFile.new(source: [".claude", "CLAUDE.md"], dest: %w[claude CLAUDE.md],
30
+ placeholder: "# My AI coding instructions\n"),
31
+ SourceFile.new(source: [".cursor", ".cursorrules"], dest: %w[cursor .cursorrules],
32
+ placeholder: "# Cursor IDE rules\n")
33
+ ].freeze
34
+
35
+ def call
36
+ prepare_profile_dir
37
+ write_manifest
38
+ STANDARD_FILES.each { |file| scaffold(file) }
39
+ scaffold_shared
40
+ @skill_names.each { |name| scaffold_skill(name) }
41
+
42
+ Result.new(profile_dir: @profile_dir, findings: @findings, created: @created)
43
+ end
44
+
45
+ private
46
+
47
+ def prepare_profile_dir
48
+ if Dir.exist?(@profile_dir)
49
+ raise Error, "Profile directory already exists: #{@profile_dir}. Use --force to overwrite." unless @force
50
+
51
+ FileUtils.rm_rf(@profile_dir)
52
+ end
53
+ FileUtils.mkdir_p(@profile_dir)
54
+ end
55
+
56
+ def write_manifest
57
+ path = File.join(@profile_dir, "sharekit.toml")
58
+ File.write(path, <<~TOML)
59
+ [profile]
60
+ name = "#{ENV.fetch("USER", "unknown")}"
61
+ version = "0.1.0"
62
+ description = "My AI coding setup"
63
+ TOML
64
+ @created << path
65
+ end
66
+
67
+ def scaffold(file)
68
+ source = File.join(@source_root, *file.source)
69
+ dest = File.join(@profile_dir, *file.dest)
70
+ FileUtils.mkdir_p(File.dirname(dest))
71
+ if File.exist?(source)
72
+ FileUtils.cp(source, dest)
73
+ scan_and_record(dest)
74
+ else
75
+ File.write(dest, file.placeholder)
76
+ end
77
+ @created << dest
78
+ end
79
+
80
+ def scaffold_shared
81
+ dir = File.join(@profile_dir, "shared")
82
+ FileUtils.mkdir_p(dir)
83
+ FileUtils.touch(File.join(dir, ".gitkeep"))
84
+ @created << dir
85
+ end
86
+
87
+ def scaffold_skill(name)
88
+ source_dir = File.join(@source_root, ".claude", "skills", name)
89
+ unless Dir.exist?(source_dir)
90
+ puts " ~ skill '#{name}' not found at #{source_dir}"
91
+ return
92
+ end
93
+
94
+ dest_base = File.join(@profile_dir, "claude", "skills", name)
95
+ Dir.glob("#{source_dir}/**/*").select { |f| File.file?(f) }.each do |source_file|
96
+ copy_skill_file(source_file, source_dir, dest_base)
97
+ end
98
+ end
99
+
100
+ def copy_skill_file(source_file, source_dir, dest_base)
101
+ dest_file = File.join(dest_base, source_file.delete_prefix("#{source_dir}/"))
102
+ FileUtils.mkdir_p(File.dirname(dest_file))
103
+ FileUtils.cp(source_file, dest_file)
104
+ scan_and_record(dest_file)
105
+ @created << dest_file
106
+ end
107
+
108
+ def scan_and_record(path)
109
+ content = File.read(path, encoding: "UTF-8")
110
+ @findings.concat(Scanner.scan(content, file: path).to_a)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharekit
4
+ module Cli
5
+ # Formats findings for terminal output and gates high-severity secrets.
6
+ module Reporter
7
+ GREEN = "\e[32m"
8
+ YELLOW = "\e[33m"
9
+ RESET = "\e[0m"
10
+
11
+ module_function
12
+
13
+ def report(findings, force: false)
14
+ if findings.empty?
15
+ puts "#{GREEN} ✓ No secrets detected.\n#{RESET}"
16
+ return
17
+ end
18
+
19
+ puts "#{YELLOW}\n ⚠ Secret patterns detected:#{RESET}"
20
+ findings.each { |finding| puts "#{YELLOW} #{format_line(finding)}#{RESET}" }
21
+ puts "#{YELLOW}\n ⚠ Review and redact secrets before pushing to a public repository.\n#{RESET}"
22
+
23
+ gate!(findings, force:)
24
+ end
25
+
26
+ # Data.define objects deconstruct into a Hash for free, so `in` can match
27
+ # directly on the fields we care about — no case/when + manual field access.
28
+ def format_line(finding)
29
+ case finding
30
+ in { file:, line:, rule:, preview: }
31
+ "#{file}:#{line} [#{rule}] #{preview}"
32
+ end
33
+ end
34
+
35
+ def gate!(findings, force:)
36
+ high = findings.select(&:high?)
37
+ return if high.empty? || force
38
+
39
+ raise Error, "Secrets export blocked: #{high.size} high-severity finding(s) detected. " \
40
+ "Review and remove secrets, or re-run with --force to override."
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "finding"
4
+
5
+ module Sharekit
6
+ module Cli
7
+ # Rule-driven secret scanner. Each rule is data (pattern + metadata), not a
8
+ # branch in an if/elsif chain — adding a rule means appending to RULES,
9
+ # never touching the scan loop.
10
+ class Scanner
11
+ Rule = Data.define(:name, :pattern, :severity, :context_before, :max_len, :leading_ellipsis)
12
+
13
+ RULES = [
14
+ Rule.new(name: "Private Key Block",
15
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
16
+ severity: :high, context_before: 0, max_len: 40, leading_ellipsis: false),
17
+ Rule.new(name: "AWS Access Key ID",
18
+ pattern: /AKIA[0-9A-Z]{16}/,
19
+ severity: :high, context_before: 5, max_len: 20, leading_ellipsis: true),
20
+ Rule.new(name: "GitHub Personal Access Token",
21
+ pattern: /ghp_[A-Za-z0-9_]{20,}/,
22
+ severity: :high, context_before: 5, max_len: 40, leading_ellipsis: true),
23
+ Rule.new(name: "GitHub Personal Access Token",
24
+ pattern: /github_pat_[A-Za-z0-9_]{20,}/,
25
+ severity: :high, context_before: 5, max_len: 40, leading_ellipsis: true),
26
+ Rule.new(name: "Slack Token",
27
+ pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/,
28
+ severity: :high, context_before: 5, max_len: 40, leading_ellipsis: true),
29
+ Rule.new(name: "Google API Key",
30
+ pattern: /AIza[0-9A-Za-z\-_]{30,40}/,
31
+ severity: :high, context_before: 5, max_len: 40, leading_ellipsis: true),
32
+ Rule.new(name: "Bearer Token",
33
+ pattern: /Bearer eyJ[A-Za-z0-9._-]*\.[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+/,
34
+ severity: :high, context_before: 0, max_len: 30, leading_ellipsis: false),
35
+ Rule.new(name: "Home Directory Path Leak",
36
+ pattern: %r{(/Users/[a-zA-Z0-9_-]+|/home/[a-zA-Z0-9_-]+)},
37
+ severity: :low, context_before: 5, max_len: 40, leading_ellipsis: true)
38
+ ].freeze
39
+
40
+ ENV_VAR_PATTERN = /(?:^|\s)(?:export\s+)?([A-Z_]+?)=(.*)$/
41
+ SENSITIVE_KEY_PATTERN = /(SECRET|TOKEN|PASSWORD|API_KEY|APIKEY|ACCESS_KEY)/i
42
+ PLACEHOLDER_PREFIXES = ['""', "''", "xxx", "<", "changeme", "your-", "your_"].freeze
43
+
44
+ class << self
45
+ # Scans +content+ line by line. Yields each Finding as it's found when
46
+ # a block is given; otherwise returns an Enumerator (same dual API as
47
+ # Array#each), so callers can stream (`each { }`) or collect (`.to_a`,
48
+ # `.first`, `.lazy`) without the scanner caring which.
49
+ def scan(content, file: nil)
50
+ return to_enum(:scan, content, file:) unless block_given?
51
+
52
+ content.each_line.with_index(1) do |raw_line, line_num|
53
+ line = raw_line.chomp
54
+ finding = rule_finding(line, file, line_num) || env_var_finding(line, file, line_num)
55
+ yield finding if finding
56
+ end
57
+ end
58
+
59
+ def truncate_preview(line, idx, context_before: 5, max_len: 40, leading_ellipsis: true)
60
+ start = [0, idx - context_before].max
61
+ finish = [line.length, idx + max_len].min
62
+ leading = leading_ellipsis && start.positive? ? "…" : ""
63
+ trailing = finish < line.length ? "…" : ""
64
+ "#{leading}#{line[start...finish]}#{trailing}"
65
+ end
66
+
67
+ private
68
+
69
+ def rule_finding(line, file, line_num)
70
+ rule = RULES.find { |r| r.pattern.match?(line) }
71
+ return unless rule
72
+
73
+ match = rule.pattern.match(line)
74
+ preview = truncate_preview(
75
+ line, match.begin(0),
76
+ context_before: rule.context_before, max_len: rule.max_len, leading_ellipsis: rule.leading_ellipsis
77
+ )
78
+ Finding.new(rule: rule.name, file:, line: line_num, preview:, severity: rule.severity)
79
+ end
80
+
81
+ def env_var_finding(line, file, line_num)
82
+ match = ENV_VAR_PATTERN.match(line)
83
+ return unless match
84
+
85
+ key_name = match[1].upcase
86
+ value = match[2]
87
+ return unless SENSITIVE_KEY_PATTERN.match?(key_name)
88
+ return if placeholder?(value)
89
+
90
+ Finding.new(rule: "Env Var: Sensitive Key", file:, line: line_num,
91
+ preview: "#{key_name}=#{truncate_value(value)}", severity: :medium)
92
+ end
93
+
94
+ def placeholder?(value)
95
+ return true if value.empty? || value == '""' || value == "''"
96
+
97
+ PLACEHOLDER_PREFIXES.any? { |prefix| value.start_with?(prefix) }
98
+ end
99
+
100
+ def truncate_value(value)
101
+ value.length > 8 ? "#{value[0, 8]}…" : value
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharekit
4
+ module Cli
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require_relative "cli/version"
5
+ require_relative "cli/finding"
6
+ require_relative "cli/scanner"
7
+ require_relative "cli/reporter"
8
+ require_relative "cli/file_lister"
9
+ require_relative "cli/init"
10
+
11
+ module Sharekit
12
+ module Cli
13
+ class Error < StandardError; end
14
+
15
+ # Thor gives us subcommand dispatch, flag parsing, and --help for free —
16
+ # the TS original hand-parses process.argv/flags itself.
17
+ class App < Thor
18
+ def self.exit_on_failure? = true
19
+
20
+ desc "scan [DIR]", "scan a directory for secret patterns"
21
+ method_option :force,
22
+ type: :boolean,
23
+ default: false,
24
+ desc: "exit 0 even if high-severity findings detected"
25
+ def scan(dir = ".")
26
+ findings = FileLister.list(dir).flat_map do |path|
27
+ Scanner.scan(File.read(path, encoding: "UTF-8"), file: path).to_a
28
+ rescue ArgumentError, Errno::ENOENT, Errno::EACCES => e
29
+ puts " ~ Skipped #{path}: #{e.class}"
30
+ []
31
+ end
32
+
33
+ Reporter.report(findings, force: options[:force])
34
+ rescue Error => e
35
+ warn e.message
36
+ exit 1
37
+ end
38
+
39
+ desc "init [SKILL...]", "scaffold a publishable sharekit profile"
40
+ method_option :force,
41
+ type: :boolean,
42
+ default: false,
43
+ desc: "overwrite existing dir; override secret blocking"
44
+ method_option :dir,
45
+ type: :string,
46
+ default: "./sharekit-profile",
47
+ desc: "profile directory to create"
48
+ def init(*skill_names)
49
+ result = Init.call(profile_dir: options[:dir], skill_names:, force: options[:force])
50
+ print_init_summary(result)
51
+ Reporter.report(result.findings, force: options[:force])
52
+ rescue Error => e
53
+ warn e.message
54
+ exit 1
55
+ end
56
+
57
+ no_commands do
58
+ def print_init_summary(result)
59
+ result.created.each { |path| puts " + #{path}" }
60
+ puts "\n ✓ Created profile at #{result.profile_dir}\n\n"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,6 @@
1
+ module Sharekit
2
+ module Cli
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sharekit-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lucas Santana
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ description: A small rule-driven CLI that scans files for private keys, cloud/API
27
+ tokens, and sensitive env vars, and blocks on high-severity findings unless --force
28
+ is given.
29
+ email:
30
+ - lucas.diassantana@gmail.com
31
+ executables:
32
+ - sharekit-cli
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".ruby-version"
37
+ - CHANGELOG.md
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - exe/sharekit-cli
42
+ - lib/sharekit/cli.rb
43
+ - lib/sharekit/cli/file_lister.rb
44
+ - lib/sharekit/cli/finding.rb
45
+ - lib/sharekit/cli/init.rb
46
+ - lib/sharekit/cli/reporter.rb
47
+ - lib/sharekit/cli/scanner.rb
48
+ - lib/sharekit/cli/version.rb
49
+ - sig/sharekit/cli.rbs
50
+ homepage: https://github.com/LucasSantana-Dev/sharekit-cli
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ allowed_push_host: https://rubygems.org
55
+ homepage_uri: https://github.com/LucasSantana-Dev/sharekit-cli
56
+ source_code_uri: https://github.com/LucasSantana-Dev/sharekit-cli/tree/main
57
+ changelog_uri: https://github.com/LucasSantana-Dev/sharekit-cli/blob/main/CHANGELOG.md
58
+ rubygems_mfa_required: 'true'
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 3.2.0
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.6.9
74
+ specification_version: 4
75
+ summary: Scan a directory for leaked secrets before you publish it.
76
+ test_files: []