@xera-ai/core 0.12.2 → 0.13.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.
@@ -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
  }
@@ -11035,7 +11038,8 @@ async function fetchCmd(argv, opts = {}) {
11035
11038
  const body = renderStoryBody(t);
11036
11039
  const storyHash = hashString(body);
11037
11040
  const acLines = parseAcLines(t.acceptanceCriteria);
11038
- const full = renderStory(t.key, t.summary, storyHash, acLines, body);
11041
+ const acSource = acLines.length > 0 ? "jira-field" : "none";
11042
+ const full = renderStory(t.key, t.summary, storyHash, acLines, acSource, body);
11039
11043
  mkdirSync12(dirname5(paths.storyPath), { recursive: true });
11040
11044
  writeFileSync13(paths.storyPath, full);
11041
11045
  const existing = readMeta(paths.metaPath);
@@ -11080,7 +11084,7 @@ function renderStoryBody(t) {
11080
11084
  return lines.join(`
11081
11085
  `);
11082
11086
  }
11083
- function renderStory(key, summary, storyHash, acLines, body) {
11087
+ function renderStory(key, summary, storyHash, acLines, acSource, body) {
11084
11088
  const yamlLines = [
11085
11089
  "---",
11086
11090
  `ticketId: ${key}`,
@@ -11092,6 +11096,7 @@ function renderStory(key, summary, storyHash, acLines, body) {
11092
11096
  for (const ac of acLines)
11093
11097
  yamlLines.push(` - ${JSON.stringify(ac)}`);
11094
11098
  }
11099
+ yamlLines.push(`acceptanceCriteriaSource: ${acSource}`);
11095
11100
  yamlLines.push("---", "");
11096
11101
  return yamlLines.join(`
11097
11102
  `) + body;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xera-ai/core",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
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.2",
35
- "@xera-ai/http": "^0.12.2",
34
+ "@xera-ai/web": "^0.13.0",
35
+ "@xera-ai/http": "^0.13.0",
36
36
  "@playwright/test": "1.60.0",
37
37
  "dotenv": "^16.0.0",
38
38
  "fflate": "0.8.3",
@@ -47,7 +47,11 @@ export async function fetchCmd(argv: string[], opts: FetchCmdOpts = {}): Promise
47
47
  const body = renderStoryBody(t);
48
48
  const storyHash = hashString(body);
49
49
  const acLines = parseAcLines(t.acceptanceCriteria);
50
- const full = renderStory(t.key, t.summary, storyHash, acLines, body);
50
+ // Track where AC came from so /xera-fetch step 3.5 (cognitive body-extraction)
51
+ // and `xera doctor --strict <TICKET>` can act on accurate provenance.
52
+ // `body-extraction` is set later by the skill if it finds AC in the description.
53
+ const acSource: 'jira-field' | 'none' = acLines.length > 0 ? 'jira-field' : 'none';
54
+ const full = renderStory(t.key, t.summary, storyHash, acLines, acSource, body);
51
55
  mkdirSync(dirname(paths.storyPath), { recursive: true });
52
56
  writeFileSync(paths.storyPath, full);
53
57
 
@@ -112,6 +116,7 @@ function renderStory(
112
116
  summary: string,
113
117
  storyHash: string,
114
118
  acLines: string[],
119
+ acSource: 'jira-field' | 'none',
115
120
  body: string,
116
121
  ): string {
117
122
  const yamlLines = [
@@ -124,6 +129,7 @@ function renderStory(
124
129
  yamlLines.push('acceptanceCriteria:');
125
130
  for (const ac of acLines) yamlLines.push(` - ${JSON.stringify(ac)}`);
126
131
  }
132
+ yamlLines.push(`acceptanceCriteriaSource: ${acSource}`);
127
133
  yamlLines.push('---', '');
128
134
  return yamlLines.join('\n') + body;
129
135
  }
@@ -50,6 +50,11 @@ interface StoryFrontmatter {
50
50
  summary: string;
51
51
  storyHash: string;
52
52
  acceptanceCriteria?: string[];
53
+ // Where AC came from. Set by `xera-internal fetch` ('jira-field' | 'none') and
54
+ // upgraded by /xera-fetch step 4 to 'body-extraction' when the skill extracts
55
+ // AC out of the description body. Optional for backward compat with
56
+ // pre-existing story.md files.
57
+ acceptanceCriteriaSource?: 'jira-field' | 'body-extraction' | 'none';
53
58
  linked_issues?: Array<{
54
59
  ticketId: string;
55
60
  relation: 'blocks' | 'duplicates' | 'relates' | 'supersedes';
@@ -125,10 +130,18 @@ function readStoryFrontmatter(repoRoot: string, ticket: string): StoryFrontmatte
125
130
 
126
131
  function readGraphInput(repoRoot: string, ticket: string): { modifiesAreas: string[] } {
127
132
  const path = join(repoRoot, '.xera', ticket, 'graph-input.json');
128
- if (!existsSync(path)) return { modifiesAreas: [] };
133
+ if (!existsSync(path)) {
134
+ console.warn(
135
+ `[graph-record fetch] graph-input.json missing for ${ticket} — modifiesAreas=[] (run step 5 of /xera-fetch to populate)`,
136
+ );
137
+ return { modifiesAreas: [] };
138
+ }
129
139
  try {
130
140
  return JSON.parse(readFileSync(path, 'utf8'));
131
- } catch {
141
+ } catch (err) {
142
+ console.warn(
143
+ `[graph-record fetch] graph-input.json invalid JSON for ${ticket} at ${path} — modifiesAreas=[] (${(err as Error).message})`,
144
+ );
132
145
  return { modifiesAreas: [] };
133
146
  }
134
147
  }