@xera-ai/core 0.9.1 → 0.9.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/dist/src/index.js CHANGED
@@ -17,6 +17,46 @@ var __export = (target, all) => {
17
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = import.meta.require;
19
19
 
20
+ // src/artifact/paths.ts
21
+ import { join } from "path";
22
+ function resolveArtifactPaths(repoRoot, ticket) {
23
+ if (!TICKET_RE.test(ticket)) {
24
+ throw new Error(`Invalid ticket key: "${ticket}" (expected e.g. JIRA-123 or SAMPLE-001)`);
25
+ }
26
+ const ticketDir = join(repoRoot, ".xera", ticket);
27
+ return {
28
+ ticketDir,
29
+ storyPath: join(ticketDir, "story.md"),
30
+ featurePath: join(ticketDir, "test.feature"),
31
+ specPath: join(ticketDir, "spec.ts"),
32
+ pageObjectsDir: join(ticketDir, "page-objects"),
33
+ runsDir: join(ticketDir, "runs"),
34
+ metaPath: join(ticketDir, "meta.json"),
35
+ statusPath: join(ticketDir, "status.json"),
36
+ logPath: join(ticketDir, "xera.log"),
37
+ lockPath: join(ticketDir, ".lock"),
38
+ authDir: join(repoRoot, ".xera", ".auth"),
39
+ runPath: (runId) => {
40
+ const runDir = join(ticketDir, "runs", runId);
41
+ return {
42
+ runDir,
43
+ reportJsonPath: join(runDir, "report.json"),
44
+ tracePath: join(runDir, "trace.zip"),
45
+ normalizedPath: join(runDir, "normalized.json"),
46
+ screenshotsDir: join(runDir, "screenshots"),
47
+ videoDir: join(runDir, "videos")
48
+ };
49
+ }
50
+ };
51
+ }
52
+ function generateRunId(now = new Date) {
53
+ return now.toISOString().replace(/[:.]/g, "-").replace("Z", "");
54
+ }
55
+ var TICKET_RE;
56
+ var init_paths = __esm(() => {
57
+ TICKET_RE = /^[A-Z][A-Z0-9_]*-\d+$|^SAMPLE-\d+$/;
58
+ });
59
+
20
60
  // src/artifact/hash.ts
21
61
  import { createHash } from "crypto";
22
62
  import { existsSync, readFileSync } from "fs";
@@ -67,42 +107,10 @@ function updateMeta(path, patch) {
67
107
  writeMeta(path, next);
68
108
  return next;
69
109
  }
70
- // src/artifact/paths.ts
71
- import { join } from "path";
72
- var TICKET_RE = /^[A-Z][A-Z0-9_]*-\d+$|^SAMPLE-\d+$/;
73
- function resolveArtifactPaths(repoRoot, ticket) {
74
- if (!TICKET_RE.test(ticket)) {
75
- throw new Error(`Invalid ticket key: "${ticket}" (expected e.g. JIRA-123 or SAMPLE-001)`);
76
- }
77
- const ticketDir = join(repoRoot, ".xera", ticket);
78
- return {
79
- ticketDir,
80
- storyPath: join(ticketDir, "story.md"),
81
- featurePath: join(ticketDir, "test.feature"),
82
- specPath: join(ticketDir, "spec.ts"),
83
- pageObjectsDir: join(ticketDir, "page-objects"),
84
- runsDir: join(ticketDir, "runs"),
85
- metaPath: join(ticketDir, "meta.json"),
86
- statusPath: join(ticketDir, "status.json"),
87
- logPath: join(ticketDir, "xera.log"),
88
- lockPath: join(ticketDir, ".lock"),
89
- authDir: join(repoRoot, ".xera", ".auth"),
90
- runPath: (runId) => {
91
- const runDir = join(ticketDir, "runs", runId);
92
- return {
93
- runDir,
94
- reportJsonPath: join(runDir, "report.json"),
95
- tracePath: join(runDir, "trace.zip"),
96
- normalizedPath: join(runDir, "normalized.json"),
97
- screenshotsDir: join(runDir, "screenshots"),
98
- videoDir: join(runDir, "videos")
99
- };
100
- }
101
- };
102
- }
103
- function generateRunId(now = new Date) {
104
- return now.toISOString().replace(/[:.]/g, "-").replace("Z", "");
105
- }
110
+
111
+ // src/index.ts
112
+ init_paths();
113
+
106
114
  // src/artifact/status.ts
107
115
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
108
116
  import { dirname as dirname2 } from "path";
@@ -680,8 +688,18 @@ function scrubBodyJson(body) {
680
688
  function scrubFreeText(s) {
681
689
  return s.replace(JWT_RE_G, REDACTED).replace(CREDIT_CARD_RE_G, REDACTED).replace(EMAIL_RE_G, REDACTED).replace(PHONE_RE_G, REDACTED);
682
690
  }
683
- // src/index.ts
684
- var VERSION2 = "0.1.0";
691
+ // src/versions.ts
692
+ import { createRequire } from "module";
693
+ var _require = createRequire(import.meta.url);
694
+ var XERA_VERSION = _require("../package.json").version;
695
+ function loadPromptsVersion() {
696
+ try {
697
+ return _require("@xera-ai/prompts/version.json").prompts;
698
+ } catch {
699
+ return "unknown";
700
+ }
701
+ }
702
+ var PROMPTS_VERSION = loadPromptsVersion();
685
703
  export {
686
704
  writeStatus,
687
705
  writeMeta,
@@ -716,7 +734,7 @@ export {
716
734
  appendHistory,
717
735
  acquireLock,
718
736
  XeraConfigSchema,
719
- VERSION2 as VERSION,
737
+ XERA_VERSION as VERSION,
720
738
  StatusJsonSchema,
721
739
  SENSITIVE_HEADERS,
722
740
  SENSITIVE_BODY_KEYS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xera-ai/core",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -25,14 +25,14 @@
25
25
  "bin"
26
26
  ],
27
27
  "scripts": {
28
- "build": "bun build ./src/index.ts ./bin/internal.ts --outdir ./dist --target bun --external @playwright/test --external @xera-ai/web --external @xera-ai/http --external zod",
28
+ "build": "bun build ./src/index.ts ./bin/internal.ts --outdir ./dist --target bun --external @playwright/test --external @xera-ai/web --external @xera-ai/http --external zod && cp -r src/graph/templates dist/bin/",
29
29
  "typecheck": "tsc --noEmit",
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "dependencies": {
33
33
  "zod": "4.4.3",
34
- "@xera-ai/web": "^0.9.1",
35
- "@xera-ai/http": "^0.9.1",
34
+ "@xera-ai/web": "^0.9.3",
35
+ "@xera-ai/http": "^0.9.3",
36
36
  "@playwright/test": "1.60.0",
37
37
  "fflate": "0.8.3",
38
38
  "yaml": "2.9.0"
@@ -6,6 +6,7 @@ import { resolveArtifactPaths } from '../artifact/paths';
6
6
  import { loadConfig } from '../config/load';
7
7
  import { createJiraClient } from '../jira/client';
8
8
  import type { JiraTicket } from '../jira/types';
9
+ import { PROMPTS_VERSION, XERA_VERSION } from '../versions';
9
10
 
10
11
  export interface FetchCmdOpts {
11
12
  cwd?: string;
@@ -54,8 +55,8 @@ export async function fetchCmd(argv: string[], opts: FetchCmdOpts = {}): Promise
54
55
  writeMeta(paths.metaPath, {
55
56
  ticket,
56
57
  adapter: 'web',
57
- xera_version: '0.1.0',
58
- prompts_version: '1.0.0',
58
+ xera_version: XERA_VERSION,
59
+ prompts_version: PROMPTS_VERSION,
59
60
  ...(existing ?? {}),
60
61
  // Re-stamp the just-fetched fields:
61
62
  story_hash: storyHash,
@@ -14,9 +14,9 @@ async function backfillTicket(repoRoot: string, ticket: string, dryRun: boolean)
14
14
  console.log(`[backfill dry-run] would backfill ${ticket}`);
15
15
  return 0;
16
16
  }
17
- // recordScriptImpl handles scenario/POM extraction.
18
- await recordScriptImpl(repoRoot, ticket);
17
+ // fetch first (establishes ticket node), then script (adds scenarios/POMs).
19
18
  await recordFetch(repoRoot, ticket);
19
+ await recordScriptImpl(repoRoot, ticket);
20
20
  return 0;
21
21
  }
22
22
 
@@ -133,9 +133,17 @@ function extractPomUsage(specContent: string): string[] {
133
133
 
134
134
  export async function recordScriptImpl(repoRoot: string, ticket: string): Promise<number> {
135
135
  const ticketDir = join(repoRoot, '.xera', ticket);
136
- const featurePath = join(ticketDir, 'feature', `${ticket}.feature`);
137
- const specPath = join(ticketDir, 'tests', `${ticket}.spec.ts`);
138
- const pomDir = join(ticketDir, 'poms');
136
+
137
+ // Try new layout first, fall back to pre-v0.6 layout for backfill compatibility
138
+ const featurePath = existsSync(join(ticketDir, 'test.feature'))
139
+ ? join(ticketDir, 'test.feature')
140
+ : join(ticketDir, 'feature', `${ticket}.feature`);
141
+ const specPath = existsSync(join(ticketDir, 'spec.ts'))
142
+ ? join(ticketDir, 'spec.ts')
143
+ : join(ticketDir, 'tests', `${ticket}.spec.ts`);
144
+ const pomDir = existsSync(join(ticketDir, 'page-objects'))
145
+ ? join(ticketDir, 'page-objects')
146
+ : join(ticketDir, 'poms');
139
147
 
140
148
  if (!existsSync(featurePath)) {
141
149
  console.error(`[graph-record script] feature missing`);
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { basename, join } from 'node:path';
4
4
  import { parse as parseYaml } from 'yaml';
5
+ import { resolveArtifactPaths } from '../artifact/paths';
5
6
  import { appendEvents } from '../graph/store';
6
7
  import type {
7
8
  Classification,
@@ -118,24 +119,24 @@ async function recordScript(repoRoot: string, ticket: string): Promise<number> {
118
119
  }
119
120
 
120
121
  async function recordExec(repoRoot: string, ticket: string, runId: string): Promise<number> {
121
- const reporterPath = join(repoRoot, '.xera', ticket, 'runs', runId, 'reporter.json');
122
- if (!existsSync(reporterPath)) {
123
- console.error(`[graph-record exec] reporter.json missing`);
122
+ const { normalizedPath } = resolveArtifactPaths(repoRoot, ticket).runPath(runId);
123
+ if (!existsSync(normalizedPath)) {
124
+ console.error(`[graph-record exec] normalized.json missing`);
124
125
  return 1;
125
126
  }
126
- const data = JSON.parse(readFileSync(reporterPath, 'utf8')) as {
127
- scenarios: Array<{ name: string; status: 'pass' | 'fail'; runtime: number; traceId?: string }>;
127
+ const data = JSON.parse(readFileSync(normalizedPath, 'utf8')) as {
128
+ scenarios: Array<{ name: string; outcome: 'PASS' | 'FAIL' | 'SKIPPED' }>;
128
129
  };
129
130
  const events: Event[] = [];
130
131
  for (const s of data.scenarios) {
132
+ if (s.outcome === 'SKIPPED') continue;
131
133
  const p: RunCompletedPayload = {
132
134
  scenarioId: scenarioId(ticket, s.name),
133
135
  ticketId: ticket,
134
136
  runId,
135
- status: s.status,
136
- runtime: s.runtime,
137
+ status: s.outcome === 'PASS' ? 'pass' : 'fail',
138
+ runtime: 0,
137
139
  };
138
- if (s.traceId) p.traceId = s.traceId;
139
140
  events.push(makeEvent('xera-exec', 'run.completed', p));
140
141
  }
141
142
  appendEvents(repoRoot, events, { skill: 'xera-exec', ticketId: ticket });
@@ -143,9 +144,10 @@ async function recordExec(repoRoot: string, ticket: string, runId: string): Prom
143
144
  }
144
145
 
145
146
  async function recordClassify(repoRoot: string, ticket: string, runId: string): Promise<number> {
146
- const classifyPath = join(repoRoot, '.xera', ticket, 'runs', runId, 'classifier-output.json');
147
+ const { ticketDir } = resolveArtifactPaths(repoRoot, ticket);
148
+ const classifyPath = join(ticketDir, 'classifier-input.json');
147
149
  if (!existsSync(classifyPath)) {
148
- console.error(`[graph-record classify] classifier-output.json missing`);
150
+ console.error(`[graph-record classify] classifier-input.json missing`);
149
151
  return 1;
150
152
  }
151
153
  const data = JSON.parse(readFileSync(classifyPath, 'utf8')) as {
@@ -14,6 +14,7 @@ import { enhanceClassification } from '../graph/classify';
14
14
  import { deriveSnapshot, loadAllEvents } from '../graph/store';
15
15
  import { buildJiraComment } from '../reporter/jira-comment';
16
16
  import { writeStatusFromClassification } from '../reporter/status-writer';
17
+ import { PROMPTS_VERSION, XERA_VERSION } from '../versions';
17
18
 
18
19
  interface ReportInput {
19
20
  scenarios: ScenarioClassification[];
@@ -187,8 +188,8 @@ export async function reportCmd(argv: string[]): Promise<number> {
187
188
  overall: reAggregated.overall,
188
189
  overallConfidence: reAggregated.overallConfidence,
189
190
  scenarios: reAggregated.scenarios,
190
- xeraVersion: '0.1.0',
191
- promptsVersion: '1.0.0',
191
+ xeraVersion: XERA_VERSION,
192
+ promptsVersion: PROMPTS_VERSION,
192
193
  });
193
194
  const draftPath = join(paths.ticketDir, 'jira-comment.draft.md');
194
195
  writeFileSync(draftPath, md);
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export const VERSION = '0.1.0';
2
1
  export type * from './adapter/types';
3
2
  export * from './artifact/hash';
4
3
  export * from './artifact/meta';
@@ -19,3 +18,4 @@ export * from './jira/types';
19
18
  export * from './lock/file-lock';
20
19
  export * from './logging/ndjson-logger';
21
20
  export * from './scrub';
21
+ export { XERA_VERSION as VERSION } from './versions';
@@ -0,0 +1,15 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ const _require = createRequire(import.meta.url);
4
+
5
+ export const XERA_VERSION = (_require('../package.json') as { version: string }).version;
6
+
7
+ function loadPromptsVersion(): string {
8
+ try {
9
+ return (_require('@xera-ai/prompts/version.json') as { prompts: string }).prompts;
10
+ } catch {
11
+ return 'unknown';
12
+ }
13
+ }
14
+
15
+ export const PROMPTS_VERSION = loadPromptsVersion();