@codyswann/lisa 4.69.4 → 4.69.5

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 (79) hide show
  1. package/cdk/create-only/stryker.conf.json +2 -0
  2. package/cdk/package-lisa/package.lisa.json +10 -6
  3. package/dist/core/lisa.d.ts +17 -3
  4. package/dist/core/lisa.d.ts.map +1 -1
  5. package/dist/core/lisa.js +74 -21
  6. package/dist/core/lisa.js.map +1 -1
  7. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  8. package/dist/core/plugin-command.d.ts +21 -0
  9. package/dist/core/plugin-command.d.ts.map +1 -0
  10. package/dist/core/plugin-command.js +121 -0
  11. package/dist/core/plugin-command.js.map +1 -0
  12. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  13. package/dist/core/upstream-evidence-manifest.js +26 -14
  14. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  15. package/eslint-plugin-component-structure/package.json +3 -0
  16. package/expo/create-only/stryker.conf.json +2 -0
  17. package/expo/package-lisa/package.lisa.json +3 -3
  18. package/harper-fabric/package-lisa/package.lisa.json +7 -7
  19. package/nestjs/package-lisa/package.lisa.json +2 -2
  20. package/package.json +28 -19
  21. package/phaser/package-lisa/package.lisa.json +7 -7
  22. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-agy/plugin.json +1 -1
  25. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-expo-agy/plugin.json +1 -1
  35. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  40. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  45. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  50. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  55. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-rails-agy/plugin.json +1 -1
  60. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  65. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  69. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  70. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  72. package/rails/copy-overwrite/.rubocop.yml +0 -2
  73. package/rails/copy-overwrite/Gemfile.lisa +8 -8
  74. package/rails/create-only/.simplecov +16 -14
  75. package/rails/create-only/spec/spec_helper.rb +2 -0
  76. package/scripts/verify-npm-advisories-4333.mjs +166 -0
  77. package/scripts/verify-rails-tooling.rb +175 -0
  78. package/typescript/create-only/stryker.conf.json +2 -0
  79. package/typescript/package-lisa/package.lisa.json +10 -6
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.69.4",
3
+ "version": "4.69.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -16,8 +16,6 @@ plugins:
16
16
  - rubocop-rspec
17
17
  - rubocop-capybara
18
18
  - rubocop-factory_bot
19
-
20
- require:
21
19
  - rubocop-yard
22
20
 
23
21
  AllCops:
@@ -13,16 +13,16 @@ group :development, :test do
13
13
  gem "rubocop-rails", "~> 2.30", require: false
14
14
  gem "rubocop-performance", "~> 1.24", require: false
15
15
  gem "rubocop-rspec", "~> 3.5", require: false
16
- gem "rubocop-capybara", "~> 2.22", require: false
16
+ gem "rubocop-capybara", "~> 3.0", require: false
17
17
  gem "rubocop-factory_bot", "~> 2.26", require: false
18
- gem "rubocop-yard", "~> 0.9", require: false
18
+ gem "rubocop-yard", "~> 1.3", require: false
19
19
 
20
20
  # Testing
21
- gem "rspec-rails", "~> 7.1"
21
+ gem "rspec-rails", "~> 8.0"
22
22
  gem "factory_bot_rails", "~> 6.4"
23
23
  gem "faker", "~> 3.5"
24
- gem "shoulda-matchers", "~> 6.4"
25
- gem "simplecov", "~> 0.22", require: false
24
+ gem "shoulda-matchers", "~> 8.0", ">= 8.0.1"
25
+ gem "simplecov", "~> 1.3", ">= 1.3.2", require: false
26
26
  gem "webmock", "~> 3.24"
27
27
  gem "vcr", "~> 6.3"
28
28
 
@@ -37,20 +37,20 @@ group :development, :test do
37
37
  gem "mutant-rspec", "~> 0.12", require: false
38
38
 
39
39
  # Security
40
- gem "brakeman", "~> 7.0", require: false
40
+ gem "brakeman", "~> 8.1", require: false
41
41
  gem "bundler-audit", "~> 0.9", require: false
42
42
 
43
43
  # Documentation
44
44
  gem "yard", "~> 0.9", require: false
45
45
 
46
46
  # Database Quality
47
- gem "database_consistency", "~> 1.7", require: false
47
+ gem "database_consistency", "~> 3.0", require: false
48
48
  end
49
49
 
50
50
  group :development do
51
51
  # Performance
52
52
  gem "bullet", "~> 8.0"
53
- gem "rack-mini-profiler", "~> 3.3"
53
+ gem "rack-mini-profiler", "~> 5.0"
54
54
 
55
55
  # Git Hooks
56
56
  gem "lefthook", require: false
@@ -9,21 +9,23 @@ thresholds = if File.exist?(thresholds_path)
9
9
  { 'line' => 80, 'branch' => 70 }
10
10
  end
11
11
 
12
- SimpleCov.start 'rails' do
13
- enable_coverage :branch
12
+ SimpleCov.load_profile 'rails'
14
13
 
15
- minimum_coverage line: thresholds['line'], branch: thresholds['branch']
14
+ SimpleCov.configure do
15
+ deprecations :raise
16
+ coverage :line, minimum: thresholds['line']
17
+ coverage :branch, minimum: thresholds['branch']
16
18
 
17
- add_group 'Models', 'app/models'
18
- add_group 'Controllers', 'app/controllers'
19
- add_group 'Services', 'app/services'
20
- add_group 'Jobs', 'app/jobs'
21
- add_group 'Mailers', 'app/mailers'
22
- add_group 'Serializers', 'app/serializers'
23
- add_group 'Libraries', 'lib'
19
+ group 'Models', 'app/models'
20
+ group 'Controllers', 'app/controllers'
21
+ group 'Services', 'app/services'
22
+ group 'Jobs', 'app/jobs'
23
+ group 'Mailers', 'app/mailers'
24
+ group 'Serializers', 'app/serializers'
25
+ group 'Libraries', 'lib'
24
26
 
25
- add_filter '/spec/'
26
- add_filter '/config/'
27
- add_filter '/db/'
28
- add_filter '/vendor/'
27
+ skip '/spec/'
28
+ skip '/config/'
29
+ skip '/db/'
30
+ skip '/vendor/'
29
31
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Seeded by Lisa on first setup — this file is YOURS.
3
4
  # Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
4
5
 
5
6
  require 'simplecov'
7
+ SimpleCov.start
6
8
 
7
9
  RSpec.configure do |config|
8
10
  config.expect_with :rspec do |expectations|
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ /** Ordinary installed compatibility smoke checks for the dependency refresh. */
3
+ import assert from "node:assert/strict";
4
+ import fs from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import path from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import {
9
+ SCRATCH_SUPERVISION_LEASE_ENV,
10
+ createSupervisedWorkerScope,
11
+ parseScratchSupervisionLease,
12
+ removeSupervisedWorkerScope,
13
+ } from "../dist/configs/vitest/scratch-supervision.js";
14
+
15
+ const root = path.resolve(process.env.LISA_NPM_PROOF_ROOT ?? ".");
16
+ const packageFile = "package.json";
17
+ const require = createRequire(path.join(root, packageFile));
18
+ const serializedLease = process.env[SCRATCH_SUPERVISION_LEASE_ENV];
19
+ assert.ok(serializedLease, "Run through lisa-test-run with its owned lease");
20
+ const lease = parseScratchSupervisionLease(serializedLease);
21
+ const scope = createSupervisedWorkerScope(lease);
22
+ const temporary = scope.path;
23
+ const checks = [];
24
+
25
+ try {
26
+ const j = require("jscodeshift").withParser("tsx");
27
+ const tree = j("const value: number = 1; const view = <div>{value}</div>;");
28
+ tree.find(j.Identifier, { name: "value" }).forEach(p => {
29
+ p.node.name = "answer";
30
+ });
31
+ assert.ok(tree.toSource().includes("{answer}"));
32
+ checks.push("jscodeshift: TSX transform");
33
+
34
+ const eslintRequire = createRequire(require.resolve("eslint"));
35
+ const Ajv6 = eslintRequire("ajv");
36
+ const validate6 = new Ajv6().compile({
37
+ type: "object",
38
+ required: ["name"],
39
+ properties: { name: { type: "string" } },
40
+ });
41
+ assert.equal(validate6({ name: "ordinary" }), true);
42
+ assert.equal(validate6({ name: 1 }), false);
43
+ const commitlintRequire = createRequire(require.resolve("@commitlint/cli"));
44
+ const Ajv8 = createRequire(
45
+ commitlintRequire.resolve("@commitlint/config-validator")
46
+ )("ajv");
47
+ const validate8 = new Ajv8.default().compile({ type: "integer" });
48
+ assert.equal(validate8(42), true);
49
+ assert.equal(validate8("42"), false);
50
+ checks.push("Ajv: distinct v6/v8 schema APIs");
51
+
52
+ const legacyMatch = eslintRequire("minimatch");
53
+ assert.equal(typeof legacyMatch, "function");
54
+ assert.equal(legacyMatch("src/file.ts", "**/*.ts"), true);
55
+ assert.equal(legacyMatch("src/file.js", "**/*.ts"), false);
56
+ const modernMatch = require("minimatch").minimatch;
57
+ assert.equal(modernMatch("src/file.ts", "**/*.ts"), true);
58
+ assert.equal(modernMatch("src/file.js", "**/*.ts"), false);
59
+ checks.push("minimatch: legacy callable and modern object APIs");
60
+
61
+ const strykerRequire = createRequire(
62
+ require.resolve("@stryker-mutator/core")
63
+ );
64
+ const restRequire = createRequire(
65
+ strykerRequire.resolve("typed-rest-client/HttpClient")
66
+ );
67
+ const qs = restRequire("qs");
68
+ assert.deepEqual(qs.parse("name=ordinary&tasks=lint&tasks=test"), {
69
+ name: "ordinary",
70
+ tasks: ["lint", "test"],
71
+ });
72
+ assert.equal(qs.stringify({ name: "ordinary" }), "name=ordinary");
73
+ assert.equal(
74
+ typeof strykerRequire("@stryker-mutator/core").Stryker,
75
+ "function"
76
+ );
77
+ checks.push(
78
+ "Stryker: installed core and compatible qs6 parse/stringify APIs"
79
+ );
80
+
81
+ const functionalPath = require.resolve("eslint-plugin-functional");
82
+ const functional = (await import(pathToFileURL(functionalPath).href)).default;
83
+ const functionalRequire = createRequire(functionalPath);
84
+ const { deepmerge } = functionalRequire("deepmerge-ts");
85
+ assert.deepEqual(
86
+ deepmerge(
87
+ { labels: ["first"], options: { enabled: true } },
88
+ { labels: ["second"], options: { level: 2 } }
89
+ ),
90
+ {
91
+ labels: ["first", "second"],
92
+ options: { enabled: true, level: 2 },
93
+ }
94
+ );
95
+ const { Linter } = require("eslint");
96
+ const linter = new Linter();
97
+ const functionalConfig = [
98
+ {
99
+ plugins: { functional },
100
+ rules: {
101
+ "functional/no-let": ["error", { ignoreIdentifierPattern: "^ignored" }],
102
+ },
103
+ },
104
+ ];
105
+ const ordinaryLet = linter.verify(
106
+ "let answer = 42; answer;",
107
+ functionalConfig
108
+ );
109
+ assert.equal(
110
+ ordinaryLet.filter(message => message.ruleId === "functional/no-let")
111
+ .length,
112
+ 1
113
+ );
114
+ assert.deepEqual(
115
+ linter.verify("const answer = 42; answer;", functionalConfig),
116
+ []
117
+ );
118
+ checks.push("functional: actual ESLint rule and installed deepmerge APIs");
119
+
120
+ const cdk = require("aws-cdk-lib");
121
+ const app = new cdk.App({ outdir: path.join(temporary, "cdk.out") });
122
+ const stack = new cdk.Stack(app, "AnonymousProof");
123
+ new cdk.aws_s3.Bucket(stack, "Storage", {
124
+ enforceSSL: true,
125
+ versioned: true,
126
+ blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
127
+ });
128
+ new cdk.CfnOutput(stack, "Proof", { value: "synth-only" });
129
+ const template = app.synth().getStackArtifact(stack.artifactId).template;
130
+ assert.equal(template.Outputs.Proof.Value, "synth-only");
131
+ assert.ok(
132
+ Object.values(template.Resources).some(
133
+ resource => resource.Type === "AWS::S3::Bucket"
134
+ )
135
+ );
136
+ checks.push("CDK: actual local synthesis without cloud operations");
137
+
138
+ const release = path.join(temporary, "release");
139
+ fs.mkdirSync(release);
140
+ fs.writeFileSync(
141
+ path.join(release, packageFile),
142
+ JSON.stringify({ name: "anonymous-release-proof", version: "1.0.0" })
143
+ );
144
+ const original = process.cwd();
145
+ process.chdir(release);
146
+ try {
147
+ await require("standard-version")({
148
+ dryRun: true,
149
+ releaseAs: "patch",
150
+ skip: { changelog: true, commit: true, tag: true },
151
+ silent: true,
152
+ });
153
+ assert.equal(
154
+ JSON.parse(fs.readFileSync(packageFile, "utf8")).version,
155
+ "1.0.0"
156
+ );
157
+ } finally {
158
+ process.chdir(original);
159
+ }
160
+ checks.push(
161
+ "standard-version: ordinary release dry run leaves manifest unchanged"
162
+ );
163
+ console.log(JSON.stringify({ success: true, checks }, null, 2));
164
+ } finally {
165
+ removeSupervisedWorkerScope(scope, lease);
166
+ }
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Run with the consumer bundle, Ruby 3.4, and an empty owned scratch directory:
4
+ # bundle exec ruby scripts/verify-rails-tooling.rb MODE TEMPLATE_ROOT SCRATCH
5
+ # Modes: bounds, configuration, coverage, coverage-negative,
6
+ # coverage-line-negative, matchers, matchers-negative, security,
7
+ # security-negative, lint, lint-negative, helper-lint.
8
+ # This bounded probe never boots Rails or connects to a database. The packaged
9
+ # Rails/MySQL journey remains a separate acceptance check.
10
+ require "fileutils"
11
+ require "json"
12
+
13
+ SELECTED = {
14
+ "brakeman" => "8.1.0",
15
+ "database_consistency" => "3.0.14",
16
+ "rack-mini-profiler" => "5.0.0",
17
+ "rspec-rails" => "8.0.4",
18
+ "rubocop-capybara" => "3.0.0",
19
+ "rubocop-yard" => "1.3.0",
20
+ "shoulda-matchers" => "8.0.1",
21
+ "simplecov" => "1.3.2"
22
+ }.freeze
23
+
24
+ mode, template_root, scratch = ARGV
25
+ abort "Expected MODE TEMPLATE_ROOT SCRATCH" unless ARGV.length == 3
26
+ template_root = File.realpath(template_root)
27
+ scratch = File.realpath(scratch)
28
+ abort "Scratch must be empty" unless Dir.children(scratch).empty?
29
+
30
+ def copy_template(root, relative, destination)
31
+ FileUtils.mkdir_p(File.dirname(destination))
32
+ FileUtils.cp(File.join(root, relative), destination)
33
+ end
34
+
35
+ Dir.chdir(scratch) do
36
+ case mode
37
+ when "bounds"
38
+ require "bundler"
39
+ definition = Bundler::Dsl.evaluate(File.join(template_root, "rails/copy-overwrite/Gemfile.lisa"), nil, {})
40
+ failures = SELECTED.filter_map do |name, selected|
41
+ dependency = definition.dependencies.find { |item| item.name == name }
42
+ abort "Missing family #{name}" unless dependency
43
+ actual = Gem.loaded_specs.fetch(name).version
44
+ abort "Wrong selected version #{name}: #{actual}" unless actual.to_s == selected
45
+ next_major = Gem::Version.new("#{actual.segments.first + 1}.0.0")
46
+ abort "Untested next major admitted for #{name}" if dependency.requirement.satisfied_by?(next_major)
47
+ "#{name}: #{dependency.requirement} rejects #{actual}" unless dependency.requirement.satisfied_by?(actual)
48
+ end
49
+ abort failures.join("\n") unless failures.empty?
50
+ { "shoulda-matchers" => "8.0.0", "simplecov" => "1.3.1" }.each do |name, old_patch|
51
+ requirement = definition.dependencies.find { |item| item.name == name }.requirement
52
+ abort "Pre-fix patch admitted for #{name}" if requirement.satisfied_by?(Gem::Version.new(old_patch))
53
+ end
54
+ puts JSON.generate(marker: "RAILS_TOOL_BOUNDS_ACCEPTED", versions: SELECTED)
55
+ when "configuration"
56
+ copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
57
+ require "simplecov/no_defaults"
58
+ require "simplecov"
59
+ SimpleCov.deprecations :raise
60
+ load ".simplecov"
61
+ abort "Configuration must not start coverage" if Coverage.running?
62
+ puts "SIMPLECOV_CONFIGURATION_ACCEPTED"
63
+ when "coverage", "coverage-negative", "coverage-line-negative"
64
+ copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
65
+ copy_template(template_root, "rails/create-only/spec/spec_helper.rb", "spec/spec_helper.rb")
66
+ FileUtils.mkdir_p("app/models")
67
+ File.write("app/models/coverage_probe.rb", <<~RUBY)
68
+ class CoverageProbe
69
+ def greeting(value)
70
+ if value == :hello
71
+ "welcome"
72
+ else
73
+ "goodbye"
74
+ end
75
+ end
76
+ def detail
77
+ text = "detail"
78
+ text.upcase
79
+ text.reverse
80
+ end
81
+ end
82
+ RUBY
83
+ require "rspec/core"
84
+ load "spec/spec_helper.rb"
85
+ SimpleCov.deprecations :raise
86
+ abort "Coverage must start in the helper" unless Coverage.running?
87
+ SimpleCov.merging false
88
+ require File.join(scratch, "app/models/coverage_probe")
89
+ probe = CoverageProbe.new
90
+ abort "Wrong exercised result" unless probe.greeting(:hello) == "welcome"
91
+ if mode != "coverage-negative"
92
+ abort "Wrong second branch" unless probe.greeting(:other) == "goodbye"
93
+ end
94
+ abort "Wrong line-control result" unless probe.detail == "liated" if mode == "coverage"
95
+ puts "COVERAGE_PROBE_EXECUTED"
96
+ when "matchers", "matchers-negative"
97
+ copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
98
+ copy_template(template_root, "rails/create-only/spec/spec_helper.rb", "spec/spec_helper.rb")
99
+ FileUtils.mkdir_p("app/models")
100
+ FileUtils.mkdir_p("spec/models")
101
+ File.write("app/models/matcher_probe.rb", <<~RUBY)
102
+ class MatcherProbe
103
+ include ActiveModel::Model
104
+ attr_accessor :name
105
+ #{'validates :name, presence: true' if mode == "matchers"}
106
+ end
107
+ RUBY
108
+ File.write("spec/models/matcher_probe_spec.rb", <<~RUBY)
109
+ require "spec_helper"
110
+ require "active_model"
111
+ require "shoulda/matchers"
112
+ Shoulda::Matchers.configure do |config|
113
+ config.integrate do |with|
114
+ with.test_framework :rspec
115
+ with.library :active_model
116
+ end
117
+ end
118
+ require File.join(Dir.pwd, "app/models/matcher_probe")
119
+ RSpec.describe MatcherProbe, type: :model do
120
+ it { is_expected.to validate_presence_of(:name) }
121
+ end
122
+ RUBY
123
+ require "rspec/core"
124
+ exit RSpec::Core::Runner.run(["--format", "documentation", "spec/models/matcher_probe_spec.rb"])
125
+ when "security", "security-negative"
126
+ FileUtils.mkdir_p(["app/controllers", "app/models", "config"])
127
+ File.write("Gemfile", "source 'https://rubygems.org'\ngem 'rails', '= 8.1.4'\n")
128
+ File.write("config/application.rb", "require 'rails/all'\nmodule ToolingProbe\nclass Application < Rails::Application\nconfig.load_defaults 8.1\nend\nend\n")
129
+ File.write("config/routes.rb", "Rails.application.routes.draw { resources :probe_records }\n")
130
+ File.write("app/controllers/application_controller.rb", "class ApplicationController < ActionController::Base\nend\n")
131
+ File.write("app/models/probe_record.rb", "class ProbeRecord < ActiveRecord::Base\nend\n")
132
+ query = if mode == "security-negative"
133
+ 'ProbeRecord.where("name = \'#{params[:name]}\'")'
134
+ else
135
+ "ProbeRecord.where(name: params[:name])"
136
+ end
137
+ File.write("app/controllers/probe_records_controller.rb", "class ProbeRecordsController < ApplicationController\n def index\n #{query}\n end\nend\n")
138
+ require "brakeman"
139
+ tracker = Brakeman.run(app_path: scratch, quiet: true, print_report: false, run_checks: ["CheckSQL"])
140
+ abort "No SQL check executed" unless tracker.checks.checks_run.include?("SQL")
141
+ abort "No model/controller inspected" if tracker.models.empty? || tracker.controllers.empty?
142
+ warnings = tracker.checks.all_warnings.select { |warning| warning.warning_type == "SQL Injection" }
143
+ puts JSON.generate(marker: "BRAKEMAN_SQL_CONTROL", checks: tracker.checks.checks_run, warnings: warnings.length, models: tracker.models.length, controllers: tracker.controllers.length)
144
+ exit(warnings.empty? ? 0 : 1)
145
+ when "helper-lint"
146
+ copy_template(template_root, "rails/copy-overwrite/.rubocop.yml", ".rubocop.yml")
147
+ [".rubocop_todo.yml", "rubocop.thresholds.yml", ".rubocop.local.yml"].each do |name|
148
+ copy_template(template_root, "rails/create-only/#{name}", name)
149
+ end
150
+ ["spec/spec_helper.rb", "spec/rails_helper.rb"].each do |name|
151
+ copy_template(template_root, "rails/create-only/#{name}", name)
152
+ end
153
+ require "rubocop"
154
+ exit RuboCop::CLI.new.run(["--format", "json", "--cache", "false", "spec/spec_helper.rb", "spec/rails_helper.rb"])
155
+ when "lint", "lint-negative"
156
+ copy_template(template_root, "rails/copy-overwrite/.rubocop.yml", ".rubocop.yml")
157
+ [".rubocop_todo.yml", "rubocop.thresholds.yml", ".rubocop.local.yml"].each do |name|
158
+ copy_template(template_root, "rails/create-only/#{name}", name)
159
+ end
160
+ FileUtils.mkdir_p("spec/features")
161
+ negative = mode == "lint-negative"
162
+ File.write("spec/features/tooling_probe_spec.rb", <<~RUBY)
163
+ #{negative ? 'all(".thing").first' : 'find(".thing")'}
164
+
165
+ # @param #{negative ? "wrong" : "name"} [String] the name
166
+ def greeting(name)
167
+ name
168
+ end
169
+ RUBY
170
+ require "rubocop"
171
+ exit RuboCop::CLI.new.run(["--only", "Capybara/FindAllFirst,YARD/MismatchName", "--format", "json", "--cache", "false", "spec/features/tooling_probe_spec.rb"])
172
+ else
173
+ abort "Unknown mode #{mode}"
174
+ end
175
+ end
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
3
3
  "testRunner": "vitest",
4
+ "_pluginsComment": "Resolve the declared runner explicitly for isolated dependency layouts instead of relying on sibling package discovery.",
5
+ "plugins": ["@stryker-mutator/vitest-runner"],
4
6
  "reporters": ["clear-text", "progress", "html"],
5
7
  "coverageAnalysis": "perTest",
6
8
  "ignoreStatic": true,
@@ -31,8 +31,8 @@
31
31
  "typecheck:lisa": "node scripts/lib/worktree-dependencies.mjs && tsc --noEmit"
32
32
  },
33
33
  "devDependencies": {
34
- "eslint-plugin-oxlint": "^1.62.0",
35
- "oxlint": "^1.62.0",
34
+ "eslint-plugin-oxlint": "1.62.0",
35
+ "oxlint": "1.62.0",
36
36
  "oxlint-tsgolint": "^0.24.0",
37
37
  "@stryker-mutator/core": "^9.0.0",
38
38
  "@stryker-mutator/vitest-runner": "^9.0.0",
@@ -49,7 +49,9 @@
49
49
  "vite": "^8.3.2",
50
50
  "ws": ">=8.21.0",
51
51
  "form-data": ">=4.0.6",
52
- "systeminformation": ">=5.31.7"
52
+ "systeminformation": ">=5.31.7",
53
+ "qs": "^6.16.0",
54
+ "deepmerge-ts": "^8.0.1"
53
55
  },
54
56
  "overrides": {
55
57
  "@isaacs/brace-expansion": "^5.0.1",
@@ -62,16 +64,18 @@
62
64
  "vite": "^8.3.2",
63
65
  "ws": ">=8.21.0",
64
66
  "form-data": ">=4.0.6",
65
- "systeminformation": ">=5.31.7"
67
+ "systeminformation": ">=5.31.7",
68
+ "qs": "^6.16.0",
69
+ "deepmerge-ts": "^8.0.1"
66
70
  }
67
71
  },
68
72
  "defaults": {
69
73
  "devDependencies": {
70
74
  "@ast-grep/cli": "^0.40.4",
71
75
  "@vitest/coverage-v8": "^4.1.11",
72
- "eslint": "^9.39.0",
76
+ "eslint": "^9.39.5",
73
77
  "husky": "^8.0.0",
74
- "prettier": "^3.3.3",
78
+ "prettier": "3.8.0",
75
79
  "typescript": "^6.0.3",
76
80
  "vitest": "^4.1.11"
77
81
  },