jev-affected 0.1.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - Read API credentials from the process environment only.
6
+ - Reject literal API keys in YAML configuration.
7
+ - Accept `TYPESAFEAI_API_KEY` as a compatibility fallback.
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  - YAML semantic task conditions, deterministic globs and protected tasks.
package/EVALUATION.md CHANGED
@@ -34,10 +34,10 @@ Run on 2026-09-21 with `jev-1.13.0` and `skipBelow: 0.10`:
34
34
  | False skips | 0 |
35
35
  | Invalid cases | 0 |
36
36
  | Cases with reduction | 6 |
37
- | Task reduction | 46.43% |
38
- | Unnecessary-run rate | 45.83% |
37
+ | Task reduction | 42.86% |
38
+ | Unnecessary-run rate | 50.00% |
39
39
  | API calls | 7 |
40
- | Total latency | 2,139 ms |
40
+ | Total latency | 2,126 ms |
41
41
  | Input tokens | 2,882 |
42
42
  | Output tokens | 476 |
43
43
 
@@ -47,7 +47,7 @@ Selected tasks by fixture:
47
47
  | --- | ---: |
48
48
  | API field | 3 / 4 |
49
49
  | Authentication | 4 / 4 |
50
- | Comment | 1 / 4 |
50
+ | Comment | 2 / 4 |
51
51
  | Database schema | 3 / 4 |
52
52
  | Documentation | 0 / 4 |
53
53
  | Logging | 1 / 4 |
@@ -65,8 +65,8 @@ pnpm eval:live
65
65
 
66
66
  `pnpm eval` uses deterministic synthetic answers and verifies evaluation and
67
67
  decision plumbing without making a network request. `pnpm eval:live` reads
68
- `TYPESAFE_API_KEY` from the process environment or a local `.env` file and sends
69
- the fixture inputs to Jev.
68
+ `TYPESAFE_API_KEY` from the process environment and sends the fixture inputs to
69
+ Jev. Dotenv files are not loaded.
70
70
 
71
71
  ## Limits
72
72
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p align="center"><strong>Semantic task routing for software development.</strong></p>
4
4
  <p align="center">Run tasks based on what changed, not where it changed.</p>
5
- <p align="center">v0.1.0 Public Beta · Node.js 20+ · TypeScript · MIT · Jev-powered</p>
5
+ <p align="center">v0.2.0 Public Beta · Node.js 20+ · TypeScript · MIT · Jev-powered</p>
6
6
 
7
7
  ![Illustrative offline fixture demo, not live Jev output](assets/demo.gif)
8
8
 
@@ -30,13 +30,7 @@ npm install -D jev-affected
30
30
  npx jev-affected init
31
31
  ```
32
32
 
33
- Create `.env` in the repository root. The CLI loads it automatically:
34
-
35
- ```dotenv
36
- TYPESAFE_API_KEY=your_api_key_here
37
- ```
38
-
39
- `.env` is ignored by this repository's generated `.gitignore`. Never put the key in `jev-affected.yml` or commit it. An existing process environment value takes precedence over `.env`. You can also set it for the current shell:
33
+ Set the API key in the process environment. The CLI does not read dotenv files or accept API keys in `jev-affected.yml`.
40
34
 
41
35
  ```powershell
42
36
  $env:TYPESAFE_API_KEY = "your_api_key_here"
@@ -46,6 +40,8 @@ $env:TYPESAFE_API_KEY = "your_api_key_here"
46
40
  export TYPESAFE_API_KEY="your_api_key_here"
47
41
  ```
48
42
 
43
+ For persistent local use, configure the variable in your shell profile or operating-system credential environment. Open a new terminal after changing persistent environment variables. `TYPESAFEAI_API_KEY` is accepted as a compatibility fallback.
44
+
49
45
  Configure your own trusted commands, commit your changes on a feature branch, then:
50
46
 
51
47
  ```sh
@@ -176,7 +172,7 @@ See [EVALUATION.md](EVALUATION.md) for the methodology, release gates, latest li
176
172
 
177
173
  ## Privacy
178
174
 
179
- No backend and no telemetry. Data is read locally; semantic inputs go directly to TypeSafe through its SDK. By default, `.env*`, PEM/key files, and names containing `credentials`, `secret` or `token` are withheld. Custom exclusions add to these defaults. Filename exclusions cannot find every secret embedded in arbitrary source files: inspect inputs and use appropriate repository practices. SDK logging is explicitly disabled.
175
+ No backend and no telemetry. Data is read locally; semantic inputs go directly to TypeSafe through its SDK. API keys are read from the process environment only. By default, `.env*`, PEM/key files, and names containing `credentials`, `secret` or `token` are withheld. Custom exclusions add to these defaults. Filename exclusions cannot find every secret embedded in arbitrary source files: inspect inputs and use appropriate repository practices. SDK logging is explicitly disabled.
180
176
 
181
177
  `TYPESAFE_BASE_URL` is honored by the official SDK; inspect this environment setting before use. Review [TypeSafe's data-processing terms](https://typesafe.ai/legal/mca) for your organization.
182
178
 
package/SECURITY.md CHANGED
@@ -10,7 +10,7 @@ Do not post secrets or exploit details in a public issue. Once the public reposi
10
10
 
11
11
  ## Secret handling
12
12
 
13
- Set TYPESAFE_API_KEY through your environment or CI secret store. The application disables SDK logging and does not emit provider error bodies. Exclusion globs are additive; they cannot detect every embedded secret. Inspect your inputs before enabling remote analysis.
13
+ Set TYPESAFE_API_KEY through the process environment or CI secret store. The CLI does not load dotenv files and rejects literal API keys in its YAML configuration. The application disables SDK logging and does not emit provider error bodies. Exclusion globs are additive; they cannot detect every embedded secret. Inspect your inputs before enabling remote analysis.
14
14
 
15
15
  ## External API calls
16
16
 
package/dist/cli.js CHANGED
@@ -4,13 +4,11 @@ import { resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { parseArgs } from "node:util";
6
6
  import { TypeSafeClient } from "@typesafe-ai/sdk";
7
- import { config as loadEnvFile } from "dotenv";
8
- import { ConfigError, loadConfig, template } from "./config.js";
7
+ import { ConfigError, loadConfig, resolveApiKey, template } from "./config.js";
9
8
  import { evaluate } from "./eval.js";
10
9
  import { executePlan } from "./executor.js";
11
10
  import { collectChanges, matches } from "./git.js";
12
11
  import { createPlan } from "./planner.js";
13
- loadEnvFile({ quiet: true });
14
12
  function render(plan) {
15
13
  return [
16
14
  `jev-affected\n\nBase ${plan.base}\nHead ${plan.head}\nModel ${plan.model ?? "not analyzed"}`,
@@ -140,11 +138,12 @@ inspect never sends requests. plan never executes task commands.`);
140
138
  return;
141
139
  }
142
140
  if (command === "doctor") {
141
+ const apiKey = resolveApiKey();
143
142
  const checks = {
144
143
  node: Number(process.versions.node.split(".")[0]) >= 20,
145
144
  config: true,
146
145
  tasks: Object.keys(config.tasks).length.toString(),
147
- apiKey: !!process.env.TYPESAFE_API_KEY?.trim(),
146
+ apiKey: !!apiKey,
148
147
  };
149
148
  try {
150
149
  await collectChanges(config, opts);
@@ -156,6 +155,7 @@ inspect never sends requests. plan never executes task commands.`);
156
155
  if (checks.apiKey) {
157
156
  try {
158
157
  await new TypeSafeClient({
158
+ apiKey,
159
159
  logLevel: "off",
160
160
  timeout: config.analysis.timeoutMs,
161
161
  retry: { maxRetries: 0 },
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export declare class ConfigError extends Error {
3
3
  }
4
+ export declare const API_KEY_VARS: readonly ["TYPESAFE_API_KEY", "TYPESAFEAI_API_KEY"];
4
5
  export declare const secretPatterns: string[];
5
6
  export declare const configSchema: z.ZodObject<{
6
7
  version: z.ZodLiteral<1>;
@@ -37,5 +38,6 @@ export declare const configSchema: z.ZodObject<{
37
38
  }, z.core.$strict>;
38
39
  export type Config = z.infer<typeof configSchema>;
39
40
  export declare function parseConfig(value: unknown): Config;
41
+ export declare function resolveApiKey(env?: NodeJS.ProcessEnv): string | undefined;
40
42
  export declare function loadConfig(path?: string): Promise<Config>;
41
43
  export declare const template = "version: 1\nmodel: jev-latest\ndefaults:\n skipBelow: 0.10\ntasks:\n unit:\n command: npm test\n when: Could this change alter runtime application behavior?\n typecheck:\n command: npm run typecheck\n always: true\n";
package/dist/config.js CHANGED
@@ -3,6 +3,7 @@ import { parse } from "yaml";
3
3
  import { z } from "zod";
4
4
  export class ConfigError extends Error {
5
5
  }
6
+ export const API_KEY_VARS = ["TYPESAFE_API_KEY", "TYPESAFEAI_API_KEY"];
6
7
  const probability = z.number().min(0).max(1);
7
8
  const limits = z
8
9
  .object({
@@ -68,11 +69,23 @@ export const configSchema = z
68
69
  })
69
70
  .strict();
70
71
  export function parseConfig(value) {
72
+ if (value && typeof value === "object" && !Array.isArray(value)) {
73
+ const source = value;
74
+ if (Object.hasOwn(source, "apiKey") || Object.hasOwn(source, "api_key"))
75
+ throw new ConfigError("API keys are not supported in jev-affected.yml. Set TYPESAFE_API_KEY in the process environment or CI secret store.");
76
+ }
71
77
  const r = configSchema.safeParse(value);
72
78
  if (!r.success)
73
79
  throw new ConfigError(r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("\n"));
74
80
  return r.data;
75
81
  }
82
+ export function resolveApiKey(env = process.env) {
83
+ for (const name of API_KEY_VARS) {
84
+ const value = env[name]?.trim();
85
+ if (value)
86
+ return value;
87
+ }
88
+ }
76
89
  export async function loadConfig(path = "jev-affected.yml") {
77
90
  try {
78
91
  return parseConfig(parse(await readFile(path, "utf8")));
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type Config, ConfigError, loadConfig, parseConfig } from "./config.js";
1
+ export { API_KEY_VARS, type Config, ConfigError, loadConfig, parseConfig, resolveApiKey, } from "./config.js";
2
2
  export { executePlan } from "./executor.js";
3
3
  export { type ChangeState, collectChanges } from "./git.js";
4
4
  export { createPlan, type Plan, type TaskDecision } from "./planner.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ConfigError, loadConfig, parseConfig } from "./config.js";
1
+ export { API_KEY_VARS, ConfigError, loadConfig, parseConfig, resolveApiKey, } from "./config.js";
2
2
  export { executePlan } from "./executor.js";
3
3
  export { collectChanges } from "./git.js";
4
4
  export { createPlan } from "./planner.js";
package/dist/planner.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
- import { parseConfig, secretPatterns } from "./config.js";
4
+ import { parseConfig, resolveApiKey, secretPatterns, } from "./config.js";
5
5
  import { collectChanges, git, matches } from "./git.js";
6
6
  import { JevProvider, } from "./provider.js";
7
7
  export async function createPlan(input) {
@@ -87,6 +87,7 @@ export async function createPlan(input) {
87
87
  questions,
88
88
  model: config.model,
89
89
  timeoutMs: config.analysis.timeoutMs,
90
+ apiKey: resolveApiKey(),
90
91
  });
91
92
  }
92
93
  if (!result.model || !/^jev-\d+\.\d+\.\d+$/.test(result.model))
@@ -4,6 +4,7 @@ export interface AnalysisInput {
4
4
  questions: Record<string, string>;
5
5
  model: string;
6
6
  timeoutMs: number;
7
+ apiKey?: string;
7
8
  }
8
9
  export interface AnalysisResult {
9
10
  model: string;
package/dist/provider.js CHANGED
@@ -2,6 +2,7 @@ import { noul, TypeSafeClient } from "@typesafe-ai/sdk";
2
2
  export class JevProvider {
3
3
  async analyze(input) {
4
4
  const client = new TypeSafeClient({
5
+ apiKey: input.apiKey,
5
6
  logLevel: "off",
6
7
  timeout: input.timeoutMs,
7
8
  retry: { maxRetries: 0 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-affected",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Semantic task routing for software development, powered by Jev.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,6 @@
46
46
  "SECURITY.md",
47
47
  "CODE_OF_CONDUCT.md",
48
48
  "EVALUATION.md",
49
- ".env.example",
50
49
  "evals",
51
50
  "skills"
52
51
  ],
@@ -65,7 +64,6 @@
65
64
  },
66
65
  "dependencies": {
67
66
  "@typesafe-ai/sdk": "^0.6.0",
68
- "dotenv": "^17.2.2",
69
67
  "picomatch": "^4.0.7",
70
68
  "yaml": "^2.9.1",
71
69
  "zod": "^4.6.5"
@@ -31,10 +31,9 @@ compute the merge base.
31
31
 
32
32
  ## Credentials and data
33
33
 
34
- Set `TYPESAFE_API_KEY` in the process environment or a repository-local `.env`
35
- file. Never print the value, commit `.env`, or store the key in
36
- `jev-affected.yml`. An existing process environment value takes precedence over
37
- `.env`.
34
+ Set `TYPESAFE_API_KEY` in the process environment or CI secret store. Never
35
+ print the value or store the key in `jev-affected.yml`. The CLI does not load
36
+ dotenv files. `TYPESAFEAI_API_KEY` is accepted as a compatibility fallback.
38
37
 
39
38
  `inspect` stays local. `plan`, `why`, and `run` can send the sanitized committed
40
39
  diff and configured semantic questions to the provider. Review `inspect` output
package/.env.example DELETED
@@ -1,3 +0,0 @@
1
- # Copy this file to .env and replace the placeholder.
2
- # .env is ignored by Git. Never commit a real API key.
3
- TYPESAFE_API_KEY=replace_with_your_typesafe_api_key