tewip-ai 0.70.5 → 0.70.7

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.
@@ -62,19 +62,52 @@ ${noModelHelp()}`);
62
62
  const fileName = framework.fileFor(scenario.id);
63
63
  const file = join(framework.testDir, fileName);
64
64
  if (existsSync(file) || (cmd.moveTo && existsSync(join(cmd.moveTo, fileName)))) {
65
- results.push({ id: scenario.id, title: scenario.title, file, outcome: { kind: 'rejected', reason: `${fileName} already exists; left untouched`, repairs: 0, passedReliably: false } });
65
+ // Every file this command writes carries "tewip" in its name, so one already sitting here is
66
+ // one of its own - an unverified draft from a run that did not finish, not a test the team
67
+ // wrote. Calling that "left untouched" sounds like a decision and hides that the file is in
68
+ // the test folder, where the suite is running it.
69
+ //
70
+ // Matched case-insensitively on the whole name rather than as a prefix, because only the
71
+ // JavaScript frameworks produce `tewip-ls-002.spec.ts`: pytest gives `test_tewip_ls_002.py`
72
+ // and JUnit `TewipLs002Test.java`, and a prefix check would have handed Python and Java teams
73
+ // the misleading message this fix exists to remove.
74
+ const mine = /tewip/i.test(fileName);
75
+ results.push({
76
+ id: scenario.id,
77
+ title: scenario.title,
78
+ file,
79
+ outcome: {
80
+ kind: 'rejected',
81
+ reason: mine
82
+ ? `${fileName} is already here from a run that did not finish, and was never verified. It is in your test folder, so your suite is running it: delete it, or move it to .tewip/drafts/, then ask again.`
83
+ : `${fileName} already exists and was written by somebody else; left untouched`,
84
+ repairs: 0,
85
+ passedReliably: false,
86
+ },
87
+ });
66
88
  continue;
67
89
  }
68
90
  console.error(` ${scenario.id} ${scenario.title}`);
69
- // Stopped part-way (Ctrl+C), a draft that was never verified must not stay in the project.
91
+ // A draft that was never verified must not stay in the project, however the run ended.
92
+ //
93
+ // SIGINT and SIGTERM were covered; nothing else was. A run of this interrupted another way -
94
+ // a closed pipe, an uncaught throw, a kill - left the draft in the **test folder**, where the
95
+ // project's own suite then runs it. Seen for real: a two-scenario run was cut short by a broken
96
+ // shell command, the suite went from green to red, and the next `write` reported the leftover as
97
+ // "already exists; left untouched", which reads like a decision rather than a casualty.
98
+ //
99
+ // `renameSync` is synchronous, so an `exit` handler can still do it: that covers every way out.
100
+ const rescue = () => {
101
+ if (!existsSync(file))
102
+ return;
103
+ mkdirSync(drafts, { recursive: true });
104
+ renameSync(file, join(drafts, fileName));
105
+ };
70
106
  const leaveNothing = () => {
71
- if (existsSync(file)) {
72
- mkdirSync(drafts, { recursive: true });
73
- renameSync(file, join(drafts, fileName));
74
- }
107
+ rescue();
75
108
  process.exit(130);
76
109
  };
77
- process.once('SIGINT', leaveNothing).once('SIGTERM', leaveNothing);
110
+ process.once('SIGINT', leaveNothing).once('SIGTERM', leaveNothing).once('exit', rescue);
78
111
  let outcome;
79
112
  try {
80
113
  ({ outcome } = await writeAndVerify(model, { framework, fileName, pages, feature: saved.feature, code, houseRules: guidanceFor(loadStandards(cmd.root), fileName) }, scenario, file, { runs: cmd.runs, repairs: cmd.repairs, mutateSource: cmd.mutateSource, log: (l) => console.error(l) }));
@@ -83,7 +116,7 @@ ${noModelHelp()}`);
83
116
  outcome = { kind: 'rejected', reason: `stopped: ${error.message}`, repairs: 0, passedReliably: false };
84
117
  }
85
118
  finally {
86
- process.off('SIGINT', leaveNothing).off('SIGTERM', leaveNothing);
119
+ process.off('SIGINT', leaveNothing).off('SIGTERM', leaveNothing).off('exit', rescue);
87
120
  }
88
121
  // Some runners take minutes per run (Angular rebuilds the app for every `ng test`): say how long
89
122
  // the rest will take, so a slow run is not mistaken for a stuck one.
@@ -54,6 +54,35 @@ export async function runJira(cmd) {
54
54
  return;
55
55
  }
56
56
  const config = jiraFromEnv();
57
+ // Without a tracker, show what would be filed anyway.
58
+ //
59
+ // This file's own comment says "It shows what it would create and creates nothing until asked",
60
+ // and the help text says "Shows what it would create; --create actually files them" - but the
61
+ // refusal below ran whatever was asked, so a person who only wanted to see the list had to go and
62
+ // get a Jira account first. `bugs`, which does the same job for failures, has always answered this
63
+ // case: "No tracker was contacted: this is what would be filed."
64
+ //
65
+ // What a dry run genuinely cannot do without a server is say which scenarios are **already**
66
+ // filed, because that answer lives in the tracker. So it says so rather than implying the whole
67
+ // list is new work.
68
+ if (!config?.token && !cmd.create) {
69
+ const planned = JSON.parse(readFileSync(cmd.planFile, 'utf8'));
70
+ const wanted = chosen(planned.plan, cmd.only);
71
+ if (wanted.length === 0)
72
+ throw new Error(cmd.only.length ? `none of ${cmd.only.join(', ')} is in ${cmd.planFile}` : 'every scenario in the plan is already covered by a test');
73
+ console.log([
74
+ `Plan: ${planned.plan.title}`,
75
+ `Project: ${cmd.projectKey || '(none given)'}`,
76
+ '',
77
+ 'No tracker was contacted: this is what would be filed.',
78
+ ...wanted.map((scenario) => ` ${scenario.id} would create: ${scenario.id} ${scenario.title}`),
79
+ '',
80
+ `${wanted.length} item(s). Tewip could not check which of these you have already filed - that answer is in the tracker, not in the plan - so read the list as everything, not as new work.`,
81
+ 'To file them, set a tracker up and add --create:',
82
+ jiraHelp(),
83
+ ].join(String.fromCharCode(10)));
84
+ return;
85
+ }
57
86
  if (!config)
58
87
  throw new Error(jiraHelp());
59
88
  if (!config.token)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tewip-ai",
3
- "version": "0.70.5",
3
+ "version": "0.70.7",
4
4
  "mcpName": "io.github.hmamut39/tewip",
5
5
  "description": "Tewip finds why each failing test failed - real bug, flaky test, environment, test data or changed selector - for any stack, from the command line, CI, or your editor.",
6
6
  "keywords": [