cto-ai-cli 3.1.0 → 4.0.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.
@@ -2053,9 +2053,9 @@ var require_dispatcher_base = __commonJS({
2053
2053
  }
2054
2054
  close(callback) {
2055
2055
  if (callback === void 0) {
2056
- return new Promise((resolve6, reject) => {
2056
+ return new Promise((resolve7, reject) => {
2057
2057
  this.close((err, data) => {
2058
- return err ? reject(err) : resolve6(data);
2058
+ return err ? reject(err) : resolve7(data);
2059
2059
  });
2060
2060
  });
2061
2061
  }
@@ -2093,12 +2093,12 @@ var require_dispatcher_base = __commonJS({
2093
2093
  err = null;
2094
2094
  }
2095
2095
  if (callback === void 0) {
2096
- return new Promise((resolve6, reject) => {
2096
+ return new Promise((resolve7, reject) => {
2097
2097
  this.destroy(err, (err2, data) => {
2098
2098
  return err2 ? (
2099
2099
  /* istanbul ignore next: should never error */
2100
2100
  reject(err2)
2101
- ) : resolve6(data);
2101
+ ) : resolve7(data);
2102
2102
  });
2103
2103
  });
2104
2104
  }
@@ -4365,8 +4365,8 @@ var require_util2 = __commonJS({
4365
4365
  function createDeferredPromise() {
4366
4366
  let res;
4367
4367
  let rej;
4368
- const promise = new Promise((resolve6, reject) => {
4369
- res = resolve6;
4368
+ const promise = new Promise((resolve7, reject) => {
4369
+ res = resolve7;
4370
4370
  rej = reject;
4371
4371
  });
4372
4372
  return { promise, resolve: res, reject: rej };
@@ -6507,12 +6507,12 @@ upgrade: ${upgrade}\r
6507
6507
  cb();
6508
6508
  }
6509
6509
  }
6510
- const waitForDrain = () => new Promise((resolve6, reject) => {
6510
+ const waitForDrain = () => new Promise((resolve7, reject) => {
6511
6511
  assert(callback === null);
6512
6512
  if (socket[kError]) {
6513
6513
  reject(socket[kError]);
6514
6514
  } else {
6515
- callback = resolve6;
6515
+ callback = resolve7;
6516
6516
  }
6517
6517
  });
6518
6518
  socket.on("close", onDrain).on("drain", onDrain);
@@ -7149,12 +7149,12 @@ var require_client_h2 = __commonJS({
7149
7149
  cb();
7150
7150
  }
7151
7151
  }
7152
- const waitForDrain = () => new Promise((resolve6, reject) => {
7152
+ const waitForDrain = () => new Promise((resolve7, reject) => {
7153
7153
  assert(callback === null);
7154
7154
  if (socket[kError]) {
7155
7155
  reject(socket[kError]);
7156
7156
  } else {
7157
- callback = resolve6;
7157
+ callback = resolve7;
7158
7158
  }
7159
7159
  });
7160
7160
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -7631,16 +7631,16 @@ var require_client = __commonJS({
7631
7631
  return this[kNeedDrain] < 2;
7632
7632
  }
7633
7633
  async [kClose]() {
7634
- return new Promise((resolve6) => {
7634
+ return new Promise((resolve7) => {
7635
7635
  if (this[kSize]) {
7636
- this[kClosedResolve] = resolve6;
7636
+ this[kClosedResolve] = resolve7;
7637
7637
  } else {
7638
- resolve6(null);
7638
+ resolve7(null);
7639
7639
  }
7640
7640
  });
7641
7641
  }
7642
7642
  async [kDestroy](err) {
7643
- return new Promise((resolve6) => {
7643
+ return new Promise((resolve7) => {
7644
7644
  const requests = this[kQueue].splice(this[kPendingIdx]);
7645
7645
  for (let i = 0; i < requests.length; i++) {
7646
7646
  const request2 = requests[i];
@@ -7651,7 +7651,7 @@ var require_client = __commonJS({
7651
7651
  this[kClosedResolve]();
7652
7652
  this[kClosedResolve] = null;
7653
7653
  }
7654
- resolve6(null);
7654
+ resolve7(null);
7655
7655
  };
7656
7656
  if (this[kHTTPContext]) {
7657
7657
  this[kHTTPContext].destroy(err, callback);
@@ -7702,7 +7702,7 @@ var require_client = __commonJS({
7702
7702
  });
7703
7703
  }
7704
7704
  try {
7705
- const socket = await new Promise((resolve6, reject) => {
7705
+ const socket = await new Promise((resolve7, reject) => {
7706
7706
  client[kConnector]({
7707
7707
  host,
7708
7708
  hostname,
@@ -7714,7 +7714,7 @@ var require_client = __commonJS({
7714
7714
  if (err) {
7715
7715
  reject(err);
7716
7716
  } else {
7717
- resolve6(socket2);
7717
+ resolve7(socket2);
7718
7718
  }
7719
7719
  });
7720
7720
  });
@@ -8051,8 +8051,8 @@ var require_pool_base = __commonJS({
8051
8051
  if (this[kQueue].isEmpty()) {
8052
8052
  await Promise.all(this[kClients].map((c) => c.close()));
8053
8053
  } else {
8054
- await new Promise((resolve6) => {
8055
- this[kClosedResolve] = resolve6;
8054
+ await new Promise((resolve7) => {
8055
+ this[kClosedResolve] = resolve7;
8056
8056
  });
8057
8057
  }
8058
8058
  }
@@ -9267,7 +9267,7 @@ var require_readable = __commonJS({
9267
9267
  if (this._readableState.closeEmitted) {
9268
9268
  return null;
9269
9269
  }
9270
- return await new Promise((resolve6, reject) => {
9270
+ return await new Promise((resolve7, reject) => {
9271
9271
  if (this[kContentLength] > limit) {
9272
9272
  this.destroy(new AbortError());
9273
9273
  }
@@ -9280,7 +9280,7 @@ var require_readable = __commonJS({
9280
9280
  if (signal?.aborted) {
9281
9281
  reject(signal.reason ?? new AbortError());
9282
9282
  } else {
9283
- resolve6(null);
9283
+ resolve7(null);
9284
9284
  }
9285
9285
  }).on("error", noop3).on("data", function(chunk) {
9286
9286
  limit -= chunk.length;
@@ -9299,7 +9299,7 @@ var require_readable = __commonJS({
9299
9299
  }
9300
9300
  async function consume(stream, type) {
9301
9301
  assert(!stream[kConsume]);
9302
- return new Promise((resolve6, reject) => {
9302
+ return new Promise((resolve7, reject) => {
9303
9303
  if (isUnusable(stream)) {
9304
9304
  const rState = stream._readableState;
9305
9305
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -9316,7 +9316,7 @@ var require_readable = __commonJS({
9316
9316
  stream[kConsume] = {
9317
9317
  type,
9318
9318
  stream,
9319
- resolve: resolve6,
9319
+ resolve: resolve7,
9320
9320
  reject,
9321
9321
  length: 0,
9322
9322
  body: []
@@ -9386,18 +9386,18 @@ var require_readable = __commonJS({
9386
9386
  return buffer;
9387
9387
  }
9388
9388
  function consumeEnd(consume2) {
9389
- const { type, body, resolve: resolve6, stream, length } = consume2;
9389
+ const { type, body, resolve: resolve7, stream, length } = consume2;
9390
9390
  try {
9391
9391
  if (type === "text") {
9392
- resolve6(chunksDecode(body, length));
9392
+ resolve7(chunksDecode(body, length));
9393
9393
  } else if (type === "json") {
9394
- resolve6(JSON.parse(chunksDecode(body, length)));
9394
+ resolve7(JSON.parse(chunksDecode(body, length)));
9395
9395
  } else if (type === "arrayBuffer") {
9396
- resolve6(chunksConcat(body, length).buffer);
9396
+ resolve7(chunksConcat(body, length).buffer);
9397
9397
  } else if (type === "blob") {
9398
- resolve6(new Blob(body, { type: stream[kContentType] }));
9398
+ resolve7(new Blob(body, { type: stream[kContentType] }));
9399
9399
  } else if (type === "bytes") {
9400
- resolve6(chunksConcat(body, length));
9400
+ resolve7(chunksConcat(body, length));
9401
9401
  }
9402
9402
  consumeFinish(consume2);
9403
9403
  } catch (err) {
@@ -9655,9 +9655,9 @@ var require_api_request = __commonJS({
9655
9655
  };
9656
9656
  function request2(opts, callback) {
9657
9657
  if (callback === void 0) {
9658
- return new Promise((resolve6, reject) => {
9658
+ return new Promise((resolve7, reject) => {
9659
9659
  request2.call(this, opts, (err, data) => {
9660
- return err ? reject(err) : resolve6(data);
9660
+ return err ? reject(err) : resolve7(data);
9661
9661
  });
9662
9662
  });
9663
9663
  }
@@ -9881,9 +9881,9 @@ var require_api_stream = __commonJS({
9881
9881
  };
9882
9882
  function stream(opts, factory, callback) {
9883
9883
  if (callback === void 0) {
9884
- return new Promise((resolve6, reject) => {
9884
+ return new Promise((resolve7, reject) => {
9885
9885
  stream.call(this, opts, factory, (err, data) => {
9886
- return err ? reject(err) : resolve6(data);
9886
+ return err ? reject(err) : resolve7(data);
9887
9887
  });
9888
9888
  });
9889
9889
  }
@@ -10168,9 +10168,9 @@ var require_api_upgrade = __commonJS({
10168
10168
  };
10169
10169
  function upgrade(opts, callback) {
10170
10170
  if (callback === void 0) {
10171
- return new Promise((resolve6, reject) => {
10171
+ return new Promise((resolve7, reject) => {
10172
10172
  upgrade.call(this, opts, (err, data) => {
10173
- return err ? reject(err) : resolve6(data);
10173
+ return err ? reject(err) : resolve7(data);
10174
10174
  });
10175
10175
  });
10176
10176
  }
@@ -10262,9 +10262,9 @@ var require_api_connect = __commonJS({
10262
10262
  };
10263
10263
  function connect(opts, callback) {
10264
10264
  if (callback === void 0) {
10265
- return new Promise((resolve6, reject) => {
10265
+ return new Promise((resolve7, reject) => {
10266
10266
  connect.call(this, opts, (err, data) => {
10267
- return err ? reject(err) : resolve6(data);
10267
+ return err ? reject(err) : resolve7(data);
10268
10268
  });
10269
10269
  });
10270
10270
  }
@@ -14126,7 +14126,7 @@ var require_fetch = __commonJS({
14126
14126
  function dispatch({ body }) {
14127
14127
  const url = requestCurrentURL(request2);
14128
14128
  const agent = fetchParams.controller.dispatcher;
14129
- return new Promise((resolve6, reject) => agent.dispatch(
14129
+ return new Promise((resolve7, reject) => agent.dispatch(
14130
14130
  {
14131
14131
  path: url.pathname + url.search,
14132
14132
  origin: url.origin,
@@ -14202,7 +14202,7 @@ var require_fetch = __commonJS({
14202
14202
  }
14203
14203
  }
14204
14204
  const onError = this.onError.bind(this);
14205
- resolve6({
14205
+ resolve7({
14206
14206
  status,
14207
14207
  statusText,
14208
14208
  headersList,
@@ -14248,7 +14248,7 @@ var require_fetch = __commonJS({
14248
14248
  for (let i = 0; i < rawHeaders.length; i += 2) {
14249
14249
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
14250
14250
  }
14251
- resolve6({
14251
+ resolve7({
14252
14252
  status,
14253
14253
  statusText: STATUS_CODES[status],
14254
14254
  headersList,
@@ -17841,8 +17841,8 @@ var require_util8 = __commonJS({
17841
17841
  return true;
17842
17842
  }
17843
17843
  function delay(ms) {
17844
- return new Promise((resolve6) => {
17845
- setTimeout(resolve6, ms).unref();
17844
+ return new Promise((resolve7) => {
17845
+ setTimeout(resolve7, ms).unref();
17846
17846
  });
17847
17847
  }
17848
17848
  module.exports = {
@@ -18646,11 +18646,11 @@ var require_lib = __commonJS({
18646
18646
  })();
18647
18647
  var __awaiter3 = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
18648
18648
  function adopt(value) {
18649
- return value instanceof P ? value : new P(function(resolve6) {
18650
- resolve6(value);
18649
+ return value instanceof P ? value : new P(function(resolve7) {
18650
+ resolve7(value);
18651
18651
  });
18652
18652
  }
18653
- return new (P || (P = Promise))(function(resolve6, reject) {
18653
+ return new (P || (P = Promise))(function(resolve7, reject) {
18654
18654
  function fulfilled(value) {
18655
18655
  try {
18656
18656
  step(generator.next(value));
@@ -18666,7 +18666,7 @@ var require_lib = __commonJS({
18666
18666
  }
18667
18667
  }
18668
18668
  function step(result) {
18669
- result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
18669
+ result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
18670
18670
  }
18671
18671
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18672
18672
  });
@@ -18753,26 +18753,26 @@ var require_lib = __commonJS({
18753
18753
  }
18754
18754
  readBody() {
18755
18755
  return __awaiter3(this, void 0, void 0, function* () {
18756
- return new Promise((resolve6) => __awaiter3(this, void 0, void 0, function* () {
18756
+ return new Promise((resolve7) => __awaiter3(this, void 0, void 0, function* () {
18757
18757
  let output = Buffer.alloc(0);
18758
18758
  this.message.on("data", (chunk) => {
18759
18759
  output = Buffer.concat([output, chunk]);
18760
18760
  });
18761
18761
  this.message.on("end", () => {
18762
- resolve6(output.toString());
18762
+ resolve7(output.toString());
18763
18763
  });
18764
18764
  }));
18765
18765
  });
18766
18766
  }
18767
18767
  readBodyBuffer() {
18768
18768
  return __awaiter3(this, void 0, void 0, function* () {
18769
- return new Promise((resolve6) => __awaiter3(this, void 0, void 0, function* () {
18769
+ return new Promise((resolve7) => __awaiter3(this, void 0, void 0, function* () {
18770
18770
  const chunks = [];
18771
18771
  this.message.on("data", (chunk) => {
18772
18772
  chunks.push(chunk);
18773
18773
  });
18774
18774
  this.message.on("end", () => {
18775
- resolve6(Buffer.concat(chunks));
18775
+ resolve7(Buffer.concat(chunks));
18776
18776
  });
18777
18777
  }));
18778
18778
  });
@@ -18980,14 +18980,14 @@ var require_lib = __commonJS({
18980
18980
  */
18981
18981
  requestRaw(info2, data) {
18982
18982
  return __awaiter3(this, void 0, void 0, function* () {
18983
- return new Promise((resolve6, reject) => {
18983
+ return new Promise((resolve7, reject) => {
18984
18984
  function callbackForResult(err, res) {
18985
18985
  if (err) {
18986
18986
  reject(err);
18987
18987
  } else if (!res) {
18988
18988
  reject(new Error("Unknown error"));
18989
18989
  } else {
18990
- resolve6(res);
18990
+ resolve7(res);
18991
18991
  }
18992
18992
  }
18993
18993
  this.requestRawWithCallback(info2, data, callbackForResult);
@@ -19231,12 +19231,12 @@ var require_lib = __commonJS({
19231
19231
  return __awaiter3(this, void 0, void 0, function* () {
19232
19232
  retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
19233
19233
  const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
19234
- return new Promise((resolve6) => setTimeout(() => resolve6(), ms));
19234
+ return new Promise((resolve7) => setTimeout(() => resolve7(), ms));
19235
19235
  });
19236
19236
  }
19237
19237
  _processResponse(res, options) {
19238
19238
  return __awaiter3(this, void 0, void 0, function* () {
19239
- return new Promise((resolve6, reject) => __awaiter3(this, void 0, void 0, function* () {
19239
+ return new Promise((resolve7, reject) => __awaiter3(this, void 0, void 0, function* () {
19240
19240
  const statusCode = res.message.statusCode || 0;
19241
19241
  const response = {
19242
19242
  statusCode,
@@ -19244,7 +19244,7 @@ var require_lib = __commonJS({
19244
19244
  headers: {}
19245
19245
  };
19246
19246
  if (statusCode === HttpCodes2.NotFound) {
19247
- resolve6(response);
19247
+ resolve7(response);
19248
19248
  }
19249
19249
  function dateTimeDeserializer(key, value) {
19250
19250
  if (typeof value === "string") {
@@ -19283,7 +19283,7 @@ var require_lib = __commonJS({
19283
19283
  err.result = response.result;
19284
19284
  reject(err);
19285
19285
  } else {
19286
- resolve6(response);
19286
+ resolve7(response);
19287
19287
  }
19288
19288
  }));
19289
19289
  });
@@ -19552,11 +19552,11 @@ import { EOL as EOL3 } from "os";
19552
19552
  import { constants, promises } from "fs";
19553
19553
  var __awaiter = function(thisArg, _arguments, P, generator) {
19554
19554
  function adopt(value) {
19555
- return value instanceof P ? value : new P(function(resolve6) {
19556
- resolve6(value);
19555
+ return value instanceof P ? value : new P(function(resolve7) {
19556
+ resolve7(value);
19557
19557
  });
19558
19558
  }
19559
- return new (P || (P = Promise))(function(resolve6, reject) {
19559
+ return new (P || (P = Promise))(function(resolve7, reject) {
19560
19560
  function fulfilled(value) {
19561
19561
  try {
19562
19562
  step(generator.next(value));
@@ -19572,7 +19572,7 @@ var __awaiter = function(thisArg, _arguments, P, generator) {
19572
19572
  }
19573
19573
  }
19574
19574
  function step(result) {
19575
- result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
19575
+ result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
19576
19576
  }
19577
19577
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19578
19578
  });
@@ -19939,11 +19939,11 @@ var httpClient = __toESM(require_lib(), 1);
19939
19939
  var import_undici2 = __toESM(require_undici(), 1);
19940
19940
  var __awaiter2 = function(thisArg, _arguments, P, generator) {
19941
19941
  function adopt(value) {
19942
- return value instanceof P ? value : new P(function(resolve6) {
19943
- resolve6(value);
19942
+ return value instanceof P ? value : new P(function(resolve7) {
19943
+ resolve7(value);
19944
19944
  });
19945
19945
  }
19946
- return new (P || (P = Promise))(function(resolve6, reject) {
19946
+ return new (P || (P = Promise))(function(resolve7, reject) {
19947
19947
  function fulfilled(value) {
19948
19948
  try {
19949
19949
  step(generator.next(value));
@@ -19959,7 +19959,7 @@ var __awaiter2 = function(thisArg, _arguments, P, generator) {
19959
19959
  }
19960
19960
  }
19961
19961
  function step(result) {
19962
- result.done ? resolve6(result.value) : adopt(result.value).then(fulfilled, rejected);
19962
+ result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
19963
19963
  }
19964
19964
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19965
19965
  });
@@ -23589,7 +23589,7 @@ function getOctokit(token, options, ...additionalPlugins) {
23589
23589
  }
23590
23590
 
23591
23591
  // src/action/index.ts
23592
- import { resolve as resolve5 } from "path";
23592
+ import { resolve as resolve6 } from "path";
23593
23593
 
23594
23594
  // src/engine/analyzer.ts
23595
23595
  import { readFile as readFile2, readdir as readdir2, stat as stat3 } from "fs/promises";
@@ -24295,11 +24295,13 @@ function mergeConfig(base, overrides) {
24295
24295
  }
24296
24296
 
24297
24297
  // src/engine/selector.ts
24298
- import { createHash as createHash2 } from "crypto";
24298
+ import { createHash as createHash3 } from "crypto";
24299
24299
 
24300
24300
  // src/govern/secrets.ts
24301
24301
  import { readFile as readFile3 } from "fs/promises";
24302
- import { resolve as resolve3, relative as relative3 } from "path";
24302
+ import { readFileSync as readFileSync2, existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
24303
+ import { resolve as resolve3, relative as relative3, join as join3, dirname as dirname2 } from "path";
24304
+ import { createHash as createHash2 } from "crypto";
24303
24305
  var BUILTIN_PATTERNS = [
24304
24306
  // API Keys
24305
24307
  { type: "api-key", source: `(?:api[_-]?key|apikey)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{20,})['"]?`, flags: "gi", severity: "critical", description: "API Key" },
@@ -24324,15 +24326,66 @@ var BUILTIN_PATTERNS = [
24324
24326
  { type: "connection-string", source: `(?:mongodb(?:\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\/\\/[^\\s'"]+:[^\\s'"]+@[^\\s'"]+`, flags: "gi", severity: "critical", description: "Database Connection String" },
24325
24327
  { type: "connection-string", source: `(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\s*[:=]\\s*['"]?([^\\s'"]{10,})['"]?`, flags: "gi", severity: "high", description: "Database URL" },
24326
24328
  // Environment variables with secrets
24327
- { type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" }
24329
+ { type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" },
24330
+ // Stripe
24331
+ { type: "api-key", source: "sk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Live Secret Key" },
24332
+ { type: "api-key", source: "pk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "high", description: "Stripe Live Publishable Key" },
24333
+ { type: "api-key", source: "rk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Restricted Key" },
24334
+ // Slack
24335
+ { type: "token", source: "xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack Bot Token" },
24336
+ { type: "token", source: "xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack User Token" },
24337
+ { type: "api-key", source: "https://hooks\\.slack\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+", flags: "g", severity: "high", description: "Slack Webhook URL" },
24338
+ // Google
24339
+ { type: "api-key", source: "AIza[0-9A-Za-z_-]{35}", flags: "g", severity: "high", description: "Google API Key" },
24340
+ { type: "token", source: "ya29\\.[0-9A-Za-z_-]+", flags: "g", severity: "high", description: "Google OAuth Token" },
24341
+ // Azure
24342
+ { type: "api-key", source: "(?:AccountKey|SharedAccessKey)\\s*=\\s*[a-zA-Z0-9+/=]{40,}", flags: "g", severity: "critical", description: "Azure Storage Key" },
24343
+ // Twilio
24344
+ { type: "api-key", source: "AC[a-f0-9]{32}", flags: "g", severity: "high", description: "Twilio Account SID" },
24345
+ // SendGrid
24346
+ { type: "api-key", source: "SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}", flags: "g", severity: "critical", description: "SendGrid API Key" },
24347
+ // JWT
24348
+ { type: "token", source: "eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}", flags: "g", severity: "high", description: "JSON Web Token" },
24349
+ // Datadog
24350
+ { type: "api-key", source: `(?:DD_API_KEY|DATADOG_API_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{32})['"]?`, flags: "gi", severity: "critical", description: "Datadog API Key" },
24351
+ { type: "api-key", source: `(?:DD_APP_KEY|DATADOG_APP_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{40})['"]?`, flags: "gi", severity: "critical", description: "Datadog App Key" },
24352
+ // Sentry
24353
+ { type: "connection-string", source: "https://[a-f0-9]{32}@[a-z0-9]+\\.ingest\\.sentry\\.io/[0-9]+", flags: "g", severity: "high", description: "Sentry DSN" },
24354
+ // Firebase
24355
+ { type: "api-key", source: `(?:FIREBASE_API_KEY|FIREBASE_KEY)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{30,})['"]?`, flags: "gi", severity: "high", description: "Firebase API Key" },
24356
+ { type: "connection-string", source: `firebase[a-z]*:\\/\\/[^\\s'"]+`, flags: "gi", severity: "high", description: "Firebase URL" },
24357
+ // Supabase
24358
+ { type: "api-key", source: "sbp_[a-f0-9]{40}", flags: "g", severity: "critical", description: "Supabase Service Key" },
24359
+ { type: "token", source: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\.[a-zA-Z0-9_-]{20,}\\.[a-zA-Z0-9_-]{20,}", flags: "g", severity: "high", description: "Supabase Anon/Service JWT" },
24360
+ // Vercel
24361
+ { type: "token", source: `(?:VERCEL_TOKEN|VERCEL_API_TOKEN)\\s*[:=]\\s*['"]?([a-zA-Z0-9]{24,})['"]?`, flags: "gi", severity: "critical", description: "Vercel Token" },
24362
+ // Heroku
24363
+ { type: "api-key", source: `(?:HEROKU_API_KEY|HEROKU_TOKEN)\\s*[:=]\\s*['"]?([a-f0-9\\-]{36,})['"]?`, flags: "gi", severity: "critical", description: "Heroku API Key" },
24364
+ // DigitalOcean
24365
+ { type: "token", source: "dop_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean Personal Access Token" },
24366
+ { type: "token", source: "doo_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean OAuth Token" },
24367
+ // Mailgun
24368
+ { type: "api-key", source: "key-[a-zA-Z0-9]{32}", flags: "g", severity: "high", description: "Mailgun API Key" },
24369
+ // PII
24370
+ { type: "pii", source: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b", flags: "g", severity: "medium", description: "Email Address (PII)" },
24371
+ { type: "pii", source: "\\b(?!000|666|9\\d{2})(\\d{3})[-.]?(?!00)(\\d{2})[-.]?(?!0000)(\\d{4})\\b", flags: "g", severity: "high", description: "Possible SSN (PII)" }
24328
24372
  ];
24373
+ var _cachedBuiltinPatterns = null;
24374
+ function getBuiltinPatterns() {
24375
+ if (!_cachedBuiltinPatterns) {
24376
+ _cachedBuiltinPatterns = BUILTIN_PATTERNS.map((def) => ({
24377
+ type: def.type,
24378
+ pattern: new RegExp(def.source, def.flags),
24379
+ severity: def.severity,
24380
+ description: def.description
24381
+ }));
24382
+ }
24383
+ return _cachedBuiltinPatterns;
24384
+ }
24329
24385
  function buildPatterns(customPatterns = []) {
24330
- const patterns = BUILTIN_PATTERNS.map((def) => ({
24331
- type: def.type,
24332
- pattern: new RegExp(def.source, def.flags),
24333
- severity: def.severity,
24334
- description: def.description
24335
- }));
24386
+ const builtins = getBuiltinPatterns();
24387
+ if (customPatterns.length === 0) return builtins;
24388
+ const patterns = [...builtins];
24336
24389
  for (const custom of customPatterns) {
24337
24390
  try {
24338
24391
  patterns.push({
@@ -24346,7 +24399,7 @@ function buildPatterns(customPatterns = []) {
24346
24399
  }
24347
24400
  return patterns;
24348
24401
  }
24349
- function scanContentForSecrets(content, filePath, customPatterns = []) {
24402
+ function scanContentForSecrets(content, filePath, customPatterns = [], extraPiiSafeDomains) {
24350
24403
  const findings = [];
24351
24404
  const lines = content.split("\n");
24352
24405
  const allPatterns = buildPatterns(customPatterns);
@@ -24358,6 +24411,7 @@ function scanContentForSecrets(content, filePath, customPatterns = []) {
24358
24411
  while ((match = secretPattern.pattern.exec(line)) !== null) {
24359
24412
  const matchText = match[0];
24360
24413
  if (isTemplateOrPlaceholder(matchText)) continue;
24414
+ if (secretPattern.type === "pii" && isSafeEmail(matchText, extraPiiSafeDomains)) continue;
24361
24415
  findings.push({
24362
24416
  type: secretPattern.type,
24363
24417
  file: filePath,
@@ -24405,6 +24459,36 @@ function isTemplateOrPlaceholder(value) {
24405
24459
  ];
24406
24460
  return placeholders.some((p) => p.test(value));
24407
24461
  }
24462
+ var PII_SAFE_EMAIL_DOMAINS = /* @__PURE__ */ new Set([
24463
+ "example.com",
24464
+ "example.org",
24465
+ "example.net",
24466
+ "test.com",
24467
+ "test.org",
24468
+ "test.net",
24469
+ "localhost",
24470
+ "localhost.localdomain",
24471
+ "email.com",
24472
+ "mail.com",
24473
+ "foo.com",
24474
+ "bar.com",
24475
+ "baz.com",
24476
+ "acme.com",
24477
+ "company.com",
24478
+ "corp.com",
24479
+ "noreply.com",
24480
+ "no-reply.com",
24481
+ "users.noreply.github.com",
24482
+ "placeholder.com"
24483
+ ]);
24484
+ function isSafeEmail(value, extraDomains) {
24485
+ const match = value.match(/@([a-zA-Z0-9.-]+)$/);
24486
+ if (!match) return false;
24487
+ const domain = match[1].toLowerCase();
24488
+ if (PII_SAFE_EMAIL_DOMAINS.has(domain)) return true;
24489
+ if (extraDomains && extraDomains.has(domain)) return true;
24490
+ return false;
24491
+ }
24408
24492
  function deduplicateFindings(findings) {
24409
24493
  const seen = /* @__PURE__ */ new Set();
24410
24494
  return findings.filter((f) => {
@@ -24414,12 +24498,281 @@ function deduplicateFindings(findings) {
24414
24498
  return true;
24415
24499
  });
24416
24500
  }
24501
+ function fingerprintFinding(f) {
24502
+ return createHash2("sha256").update(`${f.file}:${f.type}:${f.match}`).digest("hex").slice(0, 32);
24503
+ }
24504
+ function getAllowlistPath(projectPath) {
24505
+ return join3(projectPath, ".cto", "audit", "allowlist.json");
24506
+ }
24507
+ function loadAllowlist(projectPath) {
24508
+ const filePath = getAllowlistPath(projectPath);
24509
+ if (!existsSync4(filePath)) return [];
24510
+ try {
24511
+ return JSON.parse(readFileSync2(filePath, "utf-8"));
24512
+ } catch {
24513
+ return [];
24514
+ }
24515
+ }
24516
+ function filterByAllowlist(findings, projectPath) {
24517
+ const allowlist = loadAllowlist(projectPath);
24518
+ if (allowlist.length === 0) return { filtered: findings, allowed: [] };
24519
+ const allowedFingerprints = new Set(allowlist.map((e) => e.fingerprint));
24520
+ const filtered = [];
24521
+ const allowed = [];
24522
+ for (const f of findings) {
24523
+ if (allowedFingerprints.has(fingerprintFinding(f))) {
24524
+ allowed.push(f);
24525
+ } else {
24526
+ filtered.push(f);
24527
+ }
24528
+ }
24529
+ return { filtered, allowed };
24530
+ }
24531
+ function getHashCachePath(projectPath) {
24532
+ return join3(projectPath, ".cto", "audit", ".hashcache.json");
24533
+ }
24534
+ function loadHashCache(projectPath) {
24535
+ const filePath = getHashCachePath(projectPath);
24536
+ if (!existsSync4(filePath)) return {};
24537
+ try {
24538
+ return JSON.parse(readFileSync2(filePath, "utf-8"));
24539
+ } catch {
24540
+ return {};
24541
+ }
24542
+ }
24543
+ function saveHashCache(projectPath, cache) {
24544
+ const filePath = getHashCachePath(projectPath);
24545
+ mkdirSync(dirname2(filePath), { recursive: true });
24546
+ writeFileSync(filePath, JSON.stringify(cache));
24547
+ }
24548
+ function hashContent(content) {
24549
+ return createHash2("sha256").update(content).digest("hex").slice(0, 16);
24550
+ }
24551
+ function getChangedFiles(projectPath, filePaths) {
24552
+ const oldCache = loadHashCache(projectPath);
24553
+ const newCache = {};
24554
+ const changed = [];
24555
+ const unchanged = [];
24556
+ for (const fp of filePaths) {
24557
+ try {
24558
+ const content = readFileSync2(fp, "utf-8");
24559
+ const relPath = relative3(resolve3(projectPath), resolve3(fp));
24560
+ const hash = hashContent(content);
24561
+ newCache[relPath] = hash;
24562
+ if (oldCache[relPath] === hash) {
24563
+ unchanged.push(fp);
24564
+ } else {
24565
+ changed.push(fp);
24566
+ }
24567
+ } catch {
24568
+ changed.push(fp);
24569
+ }
24570
+ }
24571
+ return { changed, unchanged, cache: newCache };
24572
+ }
24573
+ var DEFAULT_AUDIT_CONFIG = {
24574
+ severityOverrides: {},
24575
+ piiSafeDomains: [],
24576
+ customPatterns: [],
24577
+ entropyThreshold: 5,
24578
+ includePII: true,
24579
+ incrementalScan: true
24580
+ };
24581
+ function getAuditConfigPath(projectPath) {
24582
+ return join3(projectPath, ".cto", "audit", "config.json");
24583
+ }
24584
+ function loadAuditConfig(projectPath) {
24585
+ const filePath = getAuditConfigPath(projectPath);
24586
+ if (!existsSync4(filePath)) return { ...DEFAULT_AUDIT_CONFIG };
24587
+ try {
24588
+ const loaded = JSON.parse(readFileSync2(filePath, "utf-8"));
24589
+ return { ...DEFAULT_AUDIT_CONFIG, ...loaded };
24590
+ } catch {
24591
+ return { ...DEFAULT_AUDIT_CONFIG };
24592
+ }
24593
+ }
24594
+ function applySeverityOverrides(findings, overrides) {
24595
+ if (Object.keys(overrides).length === 0) return findings;
24596
+ return findings.map((f) => {
24597
+ const override = overrides[f.type];
24598
+ if (override) return { ...f, severity: override };
24599
+ return f;
24600
+ });
24601
+ }
24602
+ function shannonEntropy(str) {
24603
+ const freq = /* @__PURE__ */ new Map();
24604
+ for (const ch of str) {
24605
+ freq.set(ch, (freq.get(ch) || 0) + 1);
24606
+ }
24607
+ let entropy = 0;
24608
+ for (const count of freq.values()) {
24609
+ const p = count / str.length;
24610
+ if (p > 0) entropy -= p * Math.log2(p);
24611
+ }
24612
+ return entropy;
24613
+ }
24614
+ var HIGH_ENTROPY_RE = /['"]([a-zA-Z0-9+/=_\-]{30,})['"]|=\s*['"]?([a-zA-Z0-9+/=_\-]{30,})['"]?/g;
24615
+ var ENTROPY_SKIP = [
24616
+ /^[a-f0-9]{32,}$/i,
24617
+ // hex hashes
24618
+ /^[A-Z_]{30,}$/,
24619
+ // all-caps constants
24620
+ /^[a-z_]{30,}$/,
24621
+ // all-lowercase identifiers
24622
+ /^[a-zA-Z0-9+/]+=+$/,
24623
+ // base64 padding
24624
+ /^[a-z]+[A-Z][a-zA-Z]+$/,
24625
+ // camelCase identifiers
24626
+ /sha\d+-/i
24627
+ // integrity hashes (sha256-, sha512-)
24628
+ ];
24629
+ function scanContentForHighEntropy(content, filePath, threshold = 5) {
24630
+ const findings = [];
24631
+ const lines = content.split("\n");
24632
+ for (let i = 0; i < lines.length; i++) {
24633
+ const line = lines[i];
24634
+ if (line.trim().startsWith("//") || line.trim().startsWith("#") || line.trim().startsWith("*")) continue;
24635
+ HIGH_ENTROPY_RE.lastIndex = 0;
24636
+ let match;
24637
+ while ((match = HIGH_ENTROPY_RE.exec(line)) !== null) {
24638
+ const value = match[1] || match[2];
24639
+ if (!value || value.length < 40) continue;
24640
+ if (isTemplateOrPlaceholder(value)) continue;
24641
+ if (ENTROPY_SKIP.some((p) => p.test(value))) continue;
24642
+ const entropy = shannonEntropy(value);
24643
+ if (entropy >= threshold) {
24644
+ findings.push({
24645
+ type: "high-entropy",
24646
+ file: filePath,
24647
+ line: i + 1,
24648
+ match: value,
24649
+ redacted: redactSecret(value),
24650
+ severity: entropy >= 5 ? "high" : "medium"
24651
+ });
24652
+ }
24653
+ }
24654
+ }
24655
+ return deduplicateFindings(findings);
24656
+ }
24657
+ async function auditProject(projectPath, filePaths, options = {}) {
24658
+ const savedConfig = loadAuditConfig(projectPath);
24659
+ const customPatterns = options.customPatterns ?? savedConfig.customPatterns;
24660
+ const entropyThreshold = options.entropyThreshold ?? savedConfig.entropyThreshold;
24661
+ const includePII = options.includePII ?? savedConfig.includePII;
24662
+ const useAllowlist = options.useAllowlist ?? true;
24663
+ const incrementalScan = options.incrementalScan ?? savedConfig.incrementalScan;
24664
+ const severityOverrides = options.severityOverrides ?? savedConfig.severityOverrides;
24665
+ let extraPiiDomains;
24666
+ const allExtraDomains = [...options.piiSafeDomains || [], ...savedConfig.piiSafeDomains];
24667
+ if (allExtraDomains.length > 0) {
24668
+ extraPiiDomains = new Set(allExtraDomains.map((d) => d.toLowerCase()));
24669
+ }
24670
+ let filesToScan = filePaths;
24671
+ let unchangedCount = 0;
24672
+ let newCache = null;
24673
+ if (incrementalScan) {
24674
+ const { changed, unchanged, cache } = getChangedFiles(projectPath, filePaths);
24675
+ newCache = cache;
24676
+ if (changed.length < filePaths.length) {
24677
+ filesToScan = changed;
24678
+ unchangedCount = unchanged.length;
24679
+ }
24680
+ }
24681
+ const allFindings = [];
24682
+ const filesWithSecrets = /* @__PURE__ */ new Set();
24683
+ for (const fp of filesToScan) {
24684
+ try {
24685
+ const content = await readFile3(fp, "utf-8");
24686
+ const relPath = relative3(resolve3(projectPath), resolve3(fp));
24687
+ const isTestFile = /\.(test|spec|mock)\.[jt]sx?$/.test(relPath) || relPath.includes("__tests__");
24688
+ const isDtsFile = relPath.endsWith(".d.ts");
24689
+ let findings = scanContentForSecrets(content, relPath, customPatterns, extraPiiDomains);
24690
+ if (!includePII) {
24691
+ findings = findings.filter((f) => f.type !== "pii");
24692
+ }
24693
+ const entropyFindings = isTestFile || isDtsFile ? [] : scanContentForHighEntropy(content, relPath, entropyThreshold);
24694
+ const combined = [...findings, ...entropyFindings];
24695
+ if (combined.length > 0) {
24696
+ filesWithSecrets.add(relPath);
24697
+ allFindings.push(...combined);
24698
+ }
24699
+ } catch {
24700
+ }
24701
+ }
24702
+ let finalFindings = applySeverityOverrides(allFindings, severityOverrides);
24703
+ let allowedCount = 0;
24704
+ if (useAllowlist) {
24705
+ const { filtered, allowed } = filterByAllowlist(finalFindings, projectPath);
24706
+ finalFindings = filtered;
24707
+ allowedCount = allowed.length;
24708
+ }
24709
+ finalFindings.sort((a, b) => {
24710
+ const order = { critical: 0, high: 1, medium: 2, low: 3 };
24711
+ return order[a.severity] - order[b.severity];
24712
+ });
24713
+ if (newCache) {
24714
+ saveHashCache(projectPath, newCache);
24715
+ }
24716
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
24717
+ const byType = {};
24718
+ for (const f of finalFindings) {
24719
+ bySeverity[f.severity]++;
24720
+ byType[f.type] = (byType[f.type] || 0) + 1;
24721
+ }
24722
+ const recommendations = [];
24723
+ if (bySeverity.critical > 0) {
24724
+ recommendations.push("CRITICAL: Rotate all detected credentials immediately. They may already be compromised.");
24725
+ }
24726
+ if (byType["password"] > 0) {
24727
+ recommendations.push("Move passwords to environment variables or a secrets manager (AWS Secrets Manager, Vault, etc.).");
24728
+ }
24729
+ if (byType["api-key"] > 0 || byType["aws-key"] > 0) {
24730
+ recommendations.push("Use environment variables for API keys. Never commit them to source control.");
24731
+ }
24732
+ if (byType["connection-string"] > 0) {
24733
+ recommendations.push("Database connection strings should use environment variables, not hardcoded values.");
24734
+ }
24735
+ if (byType["private-key"] > 0) {
24736
+ recommendations.push("Private keys should NEVER be in source code. Use a key management service.");
24737
+ }
24738
+ if (byType["pii"] > 0) {
24739
+ recommendations.push("PII detected. Review for GDPR/CCPA compliance. Consider data anonymization.");
24740
+ }
24741
+ if (byType["high-entropy"] > 0) {
24742
+ recommendations.push("High-entropy strings detected that may be secrets. Review manually.");
24743
+ }
24744
+ if (finalFindings.length > 0) {
24745
+ recommendations.push("Add a .gitignore entry for .env files if not already present.");
24746
+ recommendations.push("Run `npx cto-ai-cli --audit` regularly or add to CI pipeline.");
24747
+ }
24748
+ if (finalFindings.length === 0) {
24749
+ recommendations.push("No secrets detected. Great job keeping your codebase clean!");
24750
+ }
24751
+ if (allowedCount > 0) {
24752
+ recommendations.push(`${allowedCount} finding(s) skipped via allowlist (.cto/audit/allowlist.json).`);
24753
+ }
24754
+ if (unchangedCount > 0) {
24755
+ recommendations.push(`${unchangedCount} unchanged file(s) skipped (incremental scan).`);
24756
+ }
24757
+ return {
24758
+ findings: finalFindings,
24759
+ summary: {
24760
+ totalFiles: filePaths.length,
24761
+ filesScanned: filesToScan.length,
24762
+ filesWithSecrets: filesWithSecrets.size,
24763
+ totalFindings: finalFindings.length,
24764
+ bySeverity,
24765
+ byType
24766
+ },
24767
+ recommendations
24768
+ };
24769
+ }
24417
24770
 
24418
24771
  // src/engine/pruner.ts
24419
24772
  import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
24420
24773
  import { readFile as readFile4 } from "fs/promises";
24421
- import { existsSync as existsSync4 } from "fs";
24422
- import { join as join3 } from "path";
24774
+ import { existsSync as existsSync5 } from "fs";
24775
+ import { join as join4 } from "path";
24423
24776
  var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
24424
24777
  async function pruneFile(file, level) {
24425
24778
  if (level === "excluded") {
@@ -24665,9 +25018,9 @@ function addJSDoc(node, parts) {
24665
25018
  function findTsConfig(filePath) {
24666
25019
  let dir = filePath;
24667
25020
  for (let i = 0; i < 10; i++) {
24668
- dir = join3(dir, "..");
24669
- const candidate = join3(dir, "tsconfig.json");
24670
- if (existsSync4(candidate)) return candidate;
25021
+ dir = join4(dir, "..");
25022
+ const candidate = join4(dir, "tsconfig.json");
25023
+ if (existsSync5(candidate)) return candidate;
24671
25024
  }
24672
25025
  return void 0;
24673
25026
  }
@@ -24934,7 +25287,7 @@ async function selectContext(input) {
24934
25287
  );
24935
25288
  const excludedRisk = excludedFiles.length > 0 ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length) : 0;
24936
25289
  const hashInput = selectedFiles.map((f) => `${f.relativePath}:${f.pruneLevel}`).sort().join("|") + `|budget:${budget}`;
24937
- const hash = createHash2("sha256").update(hashInput).digest("hex").substring(0, 16);
25290
+ const hash = createHash3("sha256").update(hashInput).digest("hex").substring(0, 16);
24938
25291
  return {
24939
25292
  files: selectedFiles,
24940
25293
  totalTokens: usedTokens,
@@ -25578,10 +25931,182 @@ function emptyResult2(baseBranch) {
25578
25931
  };
25579
25932
  }
25580
25933
 
25934
+ // src/engine/quality-gate.ts
25935
+ import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
25936
+ import { resolve as resolve5 } from "path";
25937
+ import { existsSync as existsSync6 } from "fs";
25938
+ var DEFAULT_GATE_CONFIG = {
25939
+ threshold: 70,
25940
+ failOnSecrets: true,
25941
+ failOnRegression: true,
25942
+ regressionLimit: 5,
25943
+ baselinePath: ".cto/baseline.json",
25944
+ secretSeverities: ["critical", "high"]
25945
+ };
25946
+ async function loadBaseline(projectPath, baselinePath) {
25947
+ const filePath = resolve5(projectPath, baselinePath || ".cto/baseline.json");
25948
+ if (!existsSync6(filePath)) return null;
25949
+ try {
25950
+ const content = await readFile5(filePath, "utf-8");
25951
+ return JSON.parse(content);
25952
+ } catch {
25953
+ return null;
25954
+ }
25955
+ }
25956
+ async function saveBaseline(projectPath, score, commit, branch, baselinePath) {
25957
+ const dir = resolve5(projectPath, ".cto");
25958
+ if (!existsSync6(dir)) await mkdir2(dir, { recursive: true });
25959
+ const baseline = {
25960
+ score: score.overall,
25961
+ grade: score.grade,
25962
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25963
+ commit,
25964
+ branch,
25965
+ dimensions: {
25966
+ efficiency: score.dimensions.efficiency.score,
25967
+ coverage: score.dimensions.coverage.score,
25968
+ riskControl: score.dimensions.riskControl.score,
25969
+ structure: score.dimensions.structure.score,
25970
+ governance: score.dimensions.governance.score
25971
+ }
25972
+ };
25973
+ const filePath = resolve5(projectPath, baselinePath || ".cto/baseline.json");
25974
+ await writeFile3(filePath, JSON.stringify(baseline, null, 2));
25975
+ }
25976
+ async function runQualityGate(score, analysis, secretFindings, config = {}) {
25977
+ const cfg = { ...DEFAULT_GATE_CONFIG, ...config };
25978
+ const checks = [];
25979
+ const baseline = await loadBaseline(analysis.projectPath, cfg.baselinePath);
25980
+ const previousScore = baseline?.score ?? null;
25981
+ const delta = previousScore !== null ? score.overall - previousScore : null;
25982
+ const thresholdPassed = score.overall >= cfg.threshold;
25983
+ checks.push({
25984
+ name: "Score threshold",
25985
+ passed: thresholdPassed,
25986
+ detail: thresholdPassed ? `Score ${score.overall} \u2265 threshold ${cfg.threshold}` : `Score ${score.overall} < threshold ${cfg.threshold}`,
25987
+ severity: thresholdPassed ? "info" : "error"
25988
+ });
25989
+ const dangerousSecrets = secretFindings.filter(
25990
+ (f) => cfg.secretSeverities.includes(f.severity)
25991
+ );
25992
+ const secretsPassed = !cfg.failOnSecrets || dangerousSecrets.length === 0;
25993
+ checks.push({
25994
+ name: "No secrets detected",
25995
+ passed: secretsPassed,
25996
+ detail: secretsPassed ? "No critical/high severity secrets found" : `${dangerousSecrets.length} secret(s) with ${cfg.secretSeverities.join("/")} severity`,
25997
+ severity: secretsPassed ? "info" : "error"
25998
+ });
25999
+ let regressionPassed = true;
26000
+ if (cfg.failOnRegression && delta !== null) {
26001
+ regressionPassed = delta >= -cfg.regressionLimit;
26002
+ }
26003
+ checks.push({
26004
+ name: "No score regression",
26005
+ passed: regressionPassed,
26006
+ detail: delta !== null ? regressionPassed ? `Score changed by ${delta >= 0 ? "+" : ""}${delta} (limit: -${cfg.regressionLimit})` : `Score dropped by ${Math.abs(delta)} points (limit: -${cfg.regressionLimit})` : "No baseline found (first run)",
26007
+ severity: regressionPassed ? delta !== null && delta < 0 ? "warning" : "info" : "error"
26008
+ });
26009
+ const weakDimensions = Object.entries(score.dimensions).filter(([_, d]) => d.score < 50).map(([name]) => name);
26010
+ const dimensionsPassed = weakDimensions.length === 0;
26011
+ checks.push({
26012
+ name: "Dimension health",
26013
+ passed: dimensionsPassed,
26014
+ detail: dimensionsPassed ? "All dimensions above 50%" : `Weak dimensions: ${weakDimensions.join(", ")}`,
26015
+ severity: dimensionsPassed ? "info" : "warning"
26016
+ });
26017
+ const passed = checks.filter((c) => c.severity === "error").every((c) => c.passed);
26018
+ const prComment = generatePRComment(score, analysis, checks, baseline, delta);
26019
+ const summary2 = generateSummary(score, checks, passed);
26020
+ return {
26021
+ passed,
26022
+ score: score.overall,
26023
+ grade: score.grade,
26024
+ previousScore,
26025
+ delta,
26026
+ checks,
26027
+ baseline,
26028
+ prComment,
26029
+ summary: summary2
26030
+ };
26031
+ }
26032
+ function generatePRComment(score, analysis, checks, baseline, delta) {
26033
+ const gradeEmoji = score.grade.startsWith("A") ? "\u{1F7E2}" : score.grade.startsWith("B") ? "\u{1F535}" : score.grade.startsWith("C") ? "\u{1F7E1}" : "\u{1F534}";
26034
+ const allPassed = checks.filter((c) => c.severity === "error").every((c) => c.passed);
26035
+ const statusIcon = allPassed ? "\u2705" : "\u274C";
26036
+ const deltaStr = delta !== null ? ` (${delta >= 0 ? "+" : ""}${delta})` : "";
26037
+ const lines = [
26038
+ `## ${statusIcon} CTO Quality Gate ${allPassed ? "Passed" : "Failed"}`,
26039
+ "",
26040
+ `### ${gradeEmoji} Context Score: ${score.overall}/100 (${score.grade})${deltaStr}`,
26041
+ "",
26042
+ `> **${analysis.projectName}** \xB7 ${analysis.totalFiles} files \xB7 ${Math.round(analysis.totalTokens / 1e3)}K tokens`,
26043
+ "",
26044
+ "### Checks",
26045
+ "",
26046
+ "| Check | Status | Detail |",
26047
+ "|-------|--------|--------|"
26048
+ ];
26049
+ for (const check of checks) {
26050
+ const icon = check.passed ? "\u2705" : check.severity === "warning" ? "\u26A0\uFE0F" : "\u274C";
26051
+ lines.push(`| ${check.name} | ${icon} | ${check.detail} |`);
26052
+ }
26053
+ lines.push("");
26054
+ lines.push("### Dimensions");
26055
+ lines.push("");
26056
+ lines.push("| Dimension | Score | vs Baseline |");
26057
+ lines.push("|-----------|-------|-------------|");
26058
+ for (const [name, dim] of Object.entries(score.dimensions)) {
26059
+ const prev = baseline?.dimensions[name];
26060
+ const diff = prev !== void 0 ? dim.score - prev : null;
26061
+ const diffStr = diff !== null ? `${diff >= 0 ? "+" : ""}${diff}` : "\u2014";
26062
+ const bar = renderBar(dim.score);
26063
+ lines.push(`| ${name} | ${bar} ${dim.score}% | ${diffStr} |`);
26064
+ }
26065
+ lines.push("");
26066
+ lines.push("### Savings");
26067
+ lines.push("");
26068
+ lines.push(`| Metric | Value |`);
26069
+ lines.push(`|--------|-------|`);
26070
+ lines.push(`| Tokens saved | ${score.comparison.savedTokens.toLocaleString()} (${score.comparison.savedPercent}%) |`);
26071
+ lines.push(`| Monthly savings | $${score.comparison.monthlySavingsUSD.toFixed(2)} |`);
26072
+ if (score.insights.length > 0) {
26073
+ lines.push("");
26074
+ lines.push("### Insights");
26075
+ lines.push("");
26076
+ for (const insight of score.insights.slice(0, 5)) {
26077
+ const icon = insight.type === "strength" ? "\u2705" : insight.type === "weakness" ? "\u26A0\uFE0F" : "\u{1F4A1}";
26078
+ lines.push(`- ${icon} **${insight.title}** \u2014 ${insight.detail}`);
26079
+ }
26080
+ }
26081
+ lines.push("");
26082
+ lines.push("---");
26083
+ lines.push(`<sub>Generated by [CTO Quality Gate](https://npmjs.com/package/cto-ai-cli) \xB7 ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}</sub>`);
26084
+ return lines.join("\n");
26085
+ }
26086
+ function renderBar(score) {
26087
+ const filled = Math.round(score / 10);
26088
+ return "\u2588".repeat(filled) + "\u2591".repeat(10 - filled);
26089
+ }
26090
+ function generateSummary(score, checks, passed) {
26091
+ const status = passed ? "\u2705 PASSED" : "\u274C FAILED";
26092
+ const failedChecks = checks.filter((c) => !c.passed && c.severity === "error");
26093
+ const warnings = checks.filter((c) => !c.passed && c.severity === "warning");
26094
+ let summary2 = `Quality Gate ${status} \u2014 Score: ${score.overall}/100 (${score.grade})`;
26095
+ if (failedChecks.length > 0) {
26096
+ summary2 += `
26097
+ Failed: ${failedChecks.map((c) => c.name).join(", ")}`;
26098
+ }
26099
+ if (warnings.length > 0) {
26100
+ summary2 += `
26101
+ Warnings: ${warnings.map((c) => c.name).join(", ")}`;
26102
+ }
26103
+ return summary2;
26104
+ }
26105
+
25581
26106
  // src/action/index.ts
25582
26107
  async function run() {
25583
26108
  try {
25584
- const projectPath = resolve5(getInput("path") || ".");
26109
+ const projectPath = resolve6(getInput("path") || ".");
25585
26110
  const budget = parseInt(getInput("budget") || "50000", 10);
25586
26111
  const task = getInput("task") || "general code review and refactoring";
25587
26112
  const token = getInput("github-token") || process.env.GITHUB_TOKEN || "";
@@ -25658,11 +26183,30 @@ async function run() {
25658
26183
  `---`,
25659
26184
  `<sub>Generated by [CTO Context Score](https://github.com/cto-ai/cto-ai-cli) \xB7 ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}</sub>`
25660
26185
  ].join("\n");
26186
+ const threshold = parseInt(getInput("threshold") || "70", 10);
26187
+ const failOnSecrets = getInput("fail-on-secrets") !== "false";
26188
+ const failOnRegression = getInput("fail-on-regression") !== "false";
26189
+ const shouldSaveBaseline = getInput("save-baseline") !== "false";
26190
+ const filePaths = analysis.files.map((f) => f.path);
26191
+ const auditResult = await auditProject(projectPath, filePaths, { includePII: false });
26192
+ info(` Secrets scan: ${auditResult.summary.totalFindings} findings`);
26193
+ const gateResult = await runQualityGate(score, analysis, auditResult.findings, {
26194
+ threshold,
26195
+ failOnSecrets,
26196
+ failOnRegression
26197
+ });
26198
+ info(` Quality Gate: ${gateResult.passed ? "PASSED" : "FAILED"}`);
26199
+ if (shouldSaveBaseline) {
26200
+ await saveBaseline(projectPath, score);
26201
+ info(" Baseline saved");
26202
+ }
25661
26203
  setOutput("score", score.overall.toString());
25662
26204
  setOutput("grade", score.grade);
26205
+ setOutput("passed", gateResult.passed.toString());
25663
26206
  setOutput("saved-percent", score.comparison.savedPercent.toString());
25664
26207
  setOutput("monthly-savings", score.comparison.monthlySavingsUSD.toFixed(2));
25665
- setOutput("json", JSON.stringify({ score, benchmark }));
26208
+ setOutput("gate-summary", gateResult.summary);
26209
+ setOutput("json", JSON.stringify({ score, benchmark, gate: gateResult }));
25666
26210
  if (commentOnPR && isPR && token) {
25667
26211
  const octokit = getOctokit(token);
25668
26212
  const { owner, repo } = context2.repo;
@@ -25698,6 +26242,8 @@ async function run() {
25698
26242
  summary.addHeading(`${gradeEmoji} Context Score: ${score.overall}/100 (${score.grade})`).addRaw(body).write();
25699
26243
  if (failBelow > 0 && score.overall < failBelow) {
25700
26244
  setFailed(`Context Score ${score.overall} is below threshold ${failBelow}`);
26245
+ } else if (!gateResult.passed) {
26246
+ setFailed(gateResult.summary);
25701
26247
  }
25702
26248
  } catch (error2) {
25703
26249
  if (error2 instanceof Error) {