@xera-ai/core 0.12.1 → 0.12.3

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/bin/internal.ts CHANGED
@@ -1,19 +1,34 @@
1
1
  #!/usr/bin/env bun
2
- import { existsSync } from 'node:fs';
3
- import { config } from 'dotenv';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { config, parse } from 'dotenv';
4
4
  import { run } from '../src/bin-internal/index';
5
5
 
6
6
  // xera canonicalizes on `.env` (gitignored; see `xera init`, `xera doctor`,
7
- // scaffolded `.gitignore`). Earlier versions also loaded `.env.local` first,
8
- // which silently overrode `.env` when both files existed see issue #92. We
9
- // now load only `.env`; if `.env.local` is present, warn loudly so legacy
10
- // users migrate rather than wondering why their values are ignored.
7
+ // scaffolded `.gitignore`). The Bun runtime auto-loads dotenv files BEFORE
8
+ // this script runs, and Bun's precedence puts `.env.local` ahead of `.env`
9
+ // so a stale value in `.env.local` would silently override the canonical
10
+ // value in `.env` (issue #92, post-#103 followup).
11
+ //
12
+ // Mitigation: warn when `.env.local` exists AND surgically force `.env`'s
13
+ // values to win for any key present in both files. We touch only keys
14
+ // already in `.env.local` so shell-injected and CI-injected env vars
15
+ // (which the user did not put in `.env.local`) stay untouched.
11
16
  if (existsSync('.env.local')) {
12
17
  console.error(
13
- '\nwarning: .env.local detected but ignored. xera uses .env only ' +
14
- 'merge values from .env.local into .env and delete .env.local to silence this warning.\n',
18
+ '\nwarning: .env.local detected xera uses .env as the canonical source. ' +
19
+ 'Values in .env will be forced to win for any key in both files; ' +
20
+ 'merge values into .env and delete .env.local to silence this warning.\n',
15
21
  );
22
+ if (existsSync('.env')) {
23
+ const localKeys = Object.keys(parse(readFileSync('.env.local')));
24
+ const envValues = parse(readFileSync('.env'));
25
+ for (const k of localKeys) {
26
+ const v = envValues[k];
27
+ if (v !== undefined) process.env[k] = v;
28
+ }
29
+ }
16
30
  }
31
+ // Safety net for non-Bun invocations (Bun already auto-loaded `.env`).
17
32
  config();
18
33
 
19
34
  const code = await run(process.argv.slice(2));
@@ -8268,11 +8268,14 @@ function readStoryFrontmatter(repoRoot, ticket) {
8268
8268
  }
8269
8269
  function readGraphInput(repoRoot, ticket) {
8270
8270
  const path = join10(repoRoot, ".xera", ticket, "graph-input.json");
8271
- if (!existsSync8(path))
8271
+ if (!existsSync8(path)) {
8272
+ console.warn(`[graph-record fetch] graph-input.json missing for ${ticket} \u2014 modifiesAreas=[] (run step 5 of /xera-fetch to populate)`);
8272
8273
  return { modifiesAreas: [] };
8274
+ }
8273
8275
  try {
8274
8276
  return JSON.parse(readFileSync7(path, "utf8"));
8275
- } catch {
8277
+ } catch (err) {
8278
+ console.warn(`[graph-record fetch] graph-input.json invalid JSON for ${ticket} at ${path} \u2014 modifiesAreas=[] (${err.message})`);
8276
8279
  return { modifiesAreas: [] };
8277
8280
  }
8278
8281
  }
@@ -8559,7 +8562,7 @@ var init_graph_backfill = __esm(() => {
8559
8562
 
8560
8563
  // bin/internal.ts
8561
8564
  var import_dotenv = __toESM(require_main(), 1);
8562
- import { existsSync as existsSync33 } from "fs";
8565
+ import { existsSync as existsSync33, readFileSync as readFileSync29 } from "fs";
8563
8566
 
8564
8567
  // src/bin-internal/ac-coverage-backfill-finalize.ts
8565
8568
  init_store();
@@ -13356,8 +13359,17 @@ Commands: ${Object.keys(COMMANDS).join(", ")}`);
13356
13359
  // bin/internal.ts
13357
13360
  if (existsSync33(".env.local")) {
13358
13361
  console.error(`
13359
- warning: .env.local detected but ignored. xera uses .env only \u2014 ` + `merge values from .env.local into .env and delete .env.local to silence this warning.
13362
+ warning: .env.local detected \u2014 xera uses .env as the canonical source. ` + "Values in .env will be forced to win for any key in both files; " + `merge values into .env and delete .env.local to silence this warning.
13360
13363
  `);
13364
+ if (existsSync33(".env")) {
13365
+ const localKeys = Object.keys(import_dotenv.parse(readFileSync29(".env.local")));
13366
+ const envValues = import_dotenv.parse(readFileSync29(".env"));
13367
+ for (const k of localKeys) {
13368
+ const v = envValues[k];
13369
+ if (v !== undefined)
13370
+ process.env[k] = v;
13371
+ }
13372
+ }
13361
13373
  }
13362
13374
  import_dotenv.config();
13363
13375
  var code = await run(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xera-ai/core",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "zod": "4.4.3",
34
- "@xera-ai/web": "^0.12.1",
35
- "@xera-ai/http": "^0.12.1",
34
+ "@xera-ai/web": "^0.12.3",
35
+ "@xera-ai/http": "^0.12.3",
36
36
  "@playwright/test": "1.60.0",
37
37
  "dotenv": "^16.0.0",
38
38
  "fflate": "0.8.3",
@@ -125,10 +125,18 @@ function readStoryFrontmatter(repoRoot: string, ticket: string): StoryFrontmatte
125
125
 
126
126
  function readGraphInput(repoRoot: string, ticket: string): { modifiesAreas: string[] } {
127
127
  const path = join(repoRoot, '.xera', ticket, 'graph-input.json');
128
- if (!existsSync(path)) return { modifiesAreas: [] };
128
+ if (!existsSync(path)) {
129
+ console.warn(
130
+ `[graph-record fetch] graph-input.json missing for ${ticket} — modifiesAreas=[] (run step 5 of /xera-fetch to populate)`,
131
+ );
132
+ return { modifiesAreas: [] };
133
+ }
129
134
  try {
130
135
  return JSON.parse(readFileSync(path, 'utf8'));
131
- } catch {
136
+ } catch (err) {
137
+ console.warn(
138
+ `[graph-record fetch] graph-input.json invalid JSON for ${ticket} at ${path} — modifiesAreas=[] (${(err as Error).message})`,
139
+ );
132
140
  return { modifiesAreas: [] };
133
141
  }
134
142
  }