@lightward/mechanic-cli 0.1.1 → 0.1.4

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.
Files changed (49) hide show
  1. package/README.md +75 -37
  2. package/dist/base-command.d.ts +1 -0
  3. package/dist/base-command.d.ts.map +1 -1
  4. package/dist/base-command.js +16 -1
  5. package/dist/client.d.ts +3 -1
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +4 -1
  8. package/dist/commands/auth/login.d.ts.map +1 -1
  9. package/dist/commands/auth/login.js +6 -2
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/doctor.js +9 -7
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +12 -6
  14. package/dist/commands/tasks/bundle.d.ts.map +1 -1
  15. package/dist/commands/tasks/bundle.js +37 -5
  16. package/dist/commands/tasks/diff.d.ts.map +1 -1
  17. package/dist/commands/tasks/diff.js +2 -2
  18. package/dist/commands/tasks/new.d.ts +16 -0
  19. package/dist/commands/tasks/new.d.ts.map +1 -0
  20. package/dist/commands/tasks/new.js +104 -0
  21. package/dist/commands/tasks/open.d.ts.map +1 -1
  22. package/dist/commands/tasks/open.js +2 -2
  23. package/dist/commands/tasks/preview.d.ts +4 -0
  24. package/dist/commands/tasks/preview.d.ts.map +1 -1
  25. package/dist/commands/tasks/preview.js +43 -3
  26. package/dist/commands/tasks/publish.js +1 -1
  27. package/dist/commands/tasks/pull.d.ts.map +1 -1
  28. package/dist/commands/tasks/pull.js +8 -5
  29. package/dist/commands/tasks/push.d.ts +2 -0
  30. package/dist/commands/tasks/push.d.ts.map +1 -1
  31. package/dist/commands/tasks/push.js +22 -7
  32. package/dist/commands/tasks/status.d.ts +1 -1
  33. package/dist/commands/tasks/status.d.ts.map +1 -1
  34. package/dist/commands/tasks/status.js +26 -14
  35. package/dist/commands/tasks/unbundle.d.ts.map +1 -1
  36. package/dist/commands/tasks/unbundle.js +5 -5
  37. package/dist/commands/tasks/validate.d.ts +1 -0
  38. package/dist/commands/tasks/validate.d.ts.map +1 -1
  39. package/dist/commands/tasks/validate.js +40 -18
  40. package/dist/config.d.ts +1 -1
  41. package/dist/config.d.ts.map +1 -1
  42. package/dist/config.js +19 -11
  43. package/dist/tasks.js +2 -2
  44. package/dist/types.d.ts +12 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/update-check.d.ts +14 -0
  47. package/dist/update-check.d.ts.map +1 -0
  48. package/dist/update-check.js +136 -0
  49. package/package.json +1 -1
@@ -4,15 +4,18 @@ import path from "node:path";
4
4
  import { BaseCommand } from "../../base-command.js";
5
5
  import { CliError } from "../../errors.js";
6
6
  import { pathExists } from "../../fs.js";
7
- import { rawTaskFromHelperDir, readRawTaskFile, unbundledHelperDirForTaskFile, validateTask, } from "../../tasks.js";
7
+ import { rawTaskFromHelperDir, readRawTaskFile, resolveTaskSelector, unbundledHelperDirForTaskFile, validateTask, } from "../../tasks.js";
8
8
  function errorMessage(error) {
9
9
  return error instanceof Error ? error.message : String(error);
10
10
  }
11
+ function looksLikeValidationPath(input) {
12
+ return path.isAbsolute(input) || input.endsWith(".json") || input.includes("/") || input.includes(path.sep);
13
+ }
11
14
  export default class TasksValidate extends BaseCommand {
12
- static summary = "Validate one task file, helper directory, or directory of task JSON files.";
13
- static description = "Validate one canonical task JSON file, one helper task directory, or a directory of task JSON files.";
15
+ static summary = "Validate one local task or directory of task JSON files.";
16
+ static description = "Validate one local task slug, canonical task JSON file, helper task directory, or directory of task JSON files.";
14
17
  static args = {
15
- target: Args.string({ required: true, description: "Task JSON file, helper dir, or directory of task JSON files." }),
18
+ target: Args.string({ required: true, description: "Local task slug, task JSON file, helper dir, or directory of task JSON files." }),
16
19
  };
17
20
  static flags = {
18
21
  json: Flags.boolean({
@@ -23,25 +26,30 @@ export default class TasksValidate extends BaseCommand {
23
26
  const { args, flags } = await this.parse(TasksValidate);
24
27
  const target = path.resolve(args.target);
25
28
  const results = [];
26
- const stat = await fs.stat(target).catch((error) => {
27
- throw new CliError(`Unable to access ${target}: ${errorMessage(error)}`, 2);
28
- });
29
- if (stat.isFile()) {
29
+ const stat = await fs.stat(target).catch(() => null);
30
+ if (stat?.isFile()) {
30
31
  results.push(await this.validateTaskFile(target));
31
32
  }
32
- else if (await pathExists(path.join(target, "task.json"))) {
33
+ else if (stat?.isDirectory() && await pathExists(path.join(target, "task.json"))) {
33
34
  results.push(validateTask(await rawTaskFromHelperDir(target), target));
34
35
  }
36
+ else if (stat?.isDirectory()) {
37
+ results.push(...await this.validateTaskDirectory(target));
38
+ }
35
39
  else {
36
- const entries = await fs.readdir(target, { withFileTypes: true }).catch((error) => {
37
- throw new CliError(`Unable to read directory ${target}: ${errorMessage(error)}`, 2);
38
- });
39
- const files = entries
40
- .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
41
- .map((entry) => path.join(target, entry.name))
42
- .sort();
43
- for (const file of files) {
44
- results.push(await this.validateTaskFile(file));
40
+ if (looksLikeValidationPath(args.target)) {
41
+ throw new CliError(`Unable to access ${target}: no such file or directory`, 2);
42
+ }
43
+ const project = await this.loadProject();
44
+ const selector = await resolveTaskSelector(project, args.target, { allowHelperDir: true });
45
+ if (selector.helperDir) {
46
+ results.push(validateTask(await rawTaskFromHelperDir(selector.helperDir), selector.helperDir));
47
+ }
48
+ else if (selector.file) {
49
+ results.push(await this.validateTaskFile(selector.file));
50
+ }
51
+ else {
52
+ throw new CliError(`No local task file or helper directory found for ${args.target}.`, 2);
45
53
  }
46
54
  }
47
55
  if (results.length === 0) {
@@ -75,4 +83,18 @@ export default class TasksValidate extends BaseCommand {
75
83
  }
76
84
  return result;
77
85
  }
86
+ async validateTaskDirectory(target) {
87
+ const entries = await fs.readdir(target, { withFileTypes: true }).catch((error) => {
88
+ throw new CliError(`Unable to read directory ${target}: ${errorMessage(error)}`, 2);
89
+ });
90
+ const files = entries
91
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
92
+ .map((entry) => path.join(target, entry.name))
93
+ .sort();
94
+ const results = [];
95
+ for (const file of files) {
96
+ results.push(await this.validateTaskFile(file));
97
+ }
98
+ return results;
99
+ }
78
100
  }
package/dist/config.d.ts CHANGED
@@ -11,5 +11,5 @@ export declare function loadProject(cwd?: string): Promise<Project>;
11
11
  export declare function saveLinks(project: Project, links: LinksFile): Promise<void>;
12
12
  export declare function linkForSlug(project: Project, slug: string): LinkEntry | null;
13
13
  export declare function slugForRemoteId(project: Project, remoteId: string): string | null;
14
- export declare function updateLink(project: Project, slug: string, remoteId: string, contentHash: string): LinksFile;
14
+ export declare function updateLink(project: Project, slug: string, remoteId: string, contentHash: string, localContentHash?: string): LinksFile;
15
15
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,oBAAoB,QAAkE,CAAC;AA0BpG,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAyBzD;AA+BD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,SAA4B,GAAG,MAAM,CAG9F;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,UAAU,SAAuB,EACjC,MAAM,SAA4D,GACjE,cAAc,CAOhB;AAED,wBAAgB,UAAU,IAAI,SAAS,CAEtC;AAkED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,UAAQ,GACZ,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0BhF;AAED,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAkCvE;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjF;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE5E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAW3G"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,oBAAoB,QAAkE,CAAC;AA0BpG,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAyBzD;AA+BD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,SAA4B,GAAG,MAAM,CAG9F;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,UAAU,SAAuB,EACjC,MAAM,SAA4D,GACjE,cAAc,CAOhB;AAED,wBAAgB,UAAU,IAAI,SAAS,CAEtC;AAwED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,UAAQ,GACZ,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0BhF;AAED,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAkCvE;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjF;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE5E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,gBAAgB,CAAC,EAAE,MAAM,GACxB,SAAS,CAiBX"}
package/dist/config.js CHANGED
@@ -91,11 +91,15 @@ function normalizeLinkEntry(slug, entry) {
91
91
  if (!entry || typeof entry !== "object" || !entry.remote_id || !entry.last_remote_content_hash) {
92
92
  return null;
93
93
  }
94
- return [slug, {
95
- file: entry.file || `${DEFAULT_TASKS_DIR}/${slug}.json`,
96
- remote_id: entry.remote_id,
97
- last_remote_content_hash: entry.last_remote_content_hash,
98
- }];
94
+ const normalized = {
95
+ file: entry.file || `${DEFAULT_TASKS_DIR}/${slug}.json`,
96
+ remote_id: entry.remote_id,
97
+ last_remote_content_hash: entry.last_remote_content_hash,
98
+ };
99
+ if (typeof entry.last_local_content_hash === "string") {
100
+ normalized.last_local_content_hash = entry.last_local_content_hash;
101
+ }
102
+ return [slug, normalized];
99
103
  }
100
104
  function normalizeLegacyLinkEntry(slug, entry) {
101
105
  if (!entry || typeof entry !== "object" || !entry.task_id || !entry.last_remote_hash) {
@@ -213,15 +217,19 @@ export function linkForSlug(project, slug) {
213
217
  export function slugForRemoteId(project, remoteId) {
214
218
  return Object.entries(project.links.tasks).find(([, link]) => link.remote_id === remoteId)?.[0] || null;
215
219
  }
216
- export function updateLink(project, slug, remoteId, contentHash) {
220
+ export function updateLink(project, slug, remoteId, contentHash, localContentHash) {
221
+ const entry = {
222
+ file: `${project.tasksDirName}/${slug}.json`,
223
+ remote_id: remoteId,
224
+ last_remote_content_hash: contentHash,
225
+ };
226
+ if (localContentHash && localContentHash !== contentHash) {
227
+ entry.last_local_content_hash = localContentHash;
228
+ }
217
229
  return {
218
230
  tasks: {
219
231
  ...project.links.tasks,
220
- [slug]: {
221
- file: `${project.tasksDirName}/${slug}.json`,
222
- remote_id: remoteId,
223
- last_remote_content_hash: contentHash,
224
- },
232
+ [slug]: entry,
225
233
  },
226
234
  };
227
235
  }
package/dist/tasks.js CHANGED
@@ -217,7 +217,7 @@ async function resolveExistingPathSelector(project, input, allowHelperDir) {
217
217
  }
218
218
  throw new CliError([
219
219
  `No canonical task JSON file found for helper directory ${displayTaskPath(project, candidate)}.`,
220
- `Run "mechanic tasks bundle ${displayTaskPath(project, candidate)}" first.`,
220
+ `Run "mechanic tasks bundle ${taskSlug(path.basename(candidate))}" first.`,
221
221
  ].join("\n"), 2);
222
222
  }
223
223
  return null;
@@ -322,7 +322,7 @@ export function remoteChangedSinceLastPullDetails(force = false) {
322
322
  return force ? "remote changed since last pull; --force set" : "remote changed since last pull";
323
323
  }
324
324
  export function remoteChangedSinceLastPullMessage(relativeFile, remoteId) {
325
- return `${relativeFile} has remote changes since the last pull. Run "mechanic tasks diff ${relativeFile}" to review, then "mechanic tasks pull ${remoteId}" to keep the remote version, or re-run with --force only if the local file should win.`;
325
+ return `${relativeFile} has changes in Mechanic since the last pull. Run "mechanic tasks diff ${relativeFile}" to review, then "mechanic tasks pull ${remoteId}" to keep the Mechanic version, or re-run with --force only if the local file should win.`;
326
326
  }
327
327
  export function taskForPush(task) {
328
328
  const { enabled: _enabled, id: _id, remote_id: _remoteId, ...payload } = task;
package/dist/types.d.ts CHANGED
@@ -9,6 +9,7 @@ export type LinkEntry = {
9
9
  file: string;
10
10
  remote_id: string;
11
11
  last_remote_content_hash: string;
12
+ last_local_content_hash?: string;
12
13
  };
13
14
  export type LinksFile = {
14
15
  tasks: Record<string, LinkEntry>;
@@ -76,6 +77,17 @@ export type TaskPreviewResponse = {
76
77
  };
77
78
  warnings: string[];
78
79
  };
80
+ diagnostics?: Array<{
81
+ code: string;
82
+ severity: "info" | "warning" | "error";
83
+ message: string;
84
+ event_index?: number;
85
+ event_topic?: string | null;
86
+ task_run_index?: number;
87
+ action_run_index?: number;
88
+ action_type?: string | null;
89
+ docs_url?: string;
90
+ }>;
79
91
  events: Array<{
80
92
  topic: string;
81
93
  shopify_topic?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB,EAAE,MAAM,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,EAAE;QACJ,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,8BAA8B,EAAE,MAAM,EAAE,CAAC;KAC1C,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,aAAa,GAAG,+BAA+B,GAAG,gBAAgB,CAAC;QACzE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;KACjC,CAAC;IACF,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,2BAA2B,EAAE,OAAO,CAAC;KACtC,CAAC;IACF,WAAW,EAAE;QACX,mBAAmB,EAAE,MAAM,EAAE,CAAC;QAC9B,OAAO,EAAE;YACP,GAAG,EAAE,MAAM,EAAE,CAAC;YACd,YAAY,EAAE,MAAM,EAAE,CAAC;SACxB,CAAC;QACF,OAAO,EAAE;YACP,GAAG,EAAE,MAAM,EAAE,CAAC;YACd,YAAY,EAAE,MAAM,EAAE,CAAC;SACxB,CAAC;QACF,QAAQ,EAAE;YACR,QAAQ,EAAE,OAAO,CAAC;YAClB,oBAAoB,EAAE,OAAO,CAAC;YAC9B,MAAM,EAAE,MAAM,CAAC;YACf,OAAO,EAAE,MAAM,CAAC;SACjB,CAAC;QACF,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;QACvC,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;QACjC,SAAS,EAAE,KAAK,CAAC;YACf,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;YACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;YAC7C,WAAW,EAAE,KAAK,CAAC;gBACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;gBAC5B,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;gBACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;gBACtB,MAAM,CAAC,EAAE,OAAO,CAAC;gBACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;aAC9C,CAAC,CAAC;SACJ,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE;QACJ,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;IACF,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,cAAc,EAAE;YAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAChE,OAAO,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACzE,SAAS,EAAE;YAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACxG,CAAC;IACF,GAAG,EAAE;QACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export type UpdateNotice = {
2
+ currentVersion: string;
3
+ latestVersion: string;
4
+ };
5
+ export type UpdateNoticeOptions = {
6
+ cachePath?: string;
7
+ currentVersion?: string;
8
+ fetchLatestVersion?: () => Promise<string | null>;
9
+ now?: Date;
10
+ };
11
+ export declare function isVersionNewer(candidate: string, current: string): boolean;
12
+ export declare function getUpdateNotice(options?: UpdateNoticeOptions): Promise<UpdateNotice | null>;
13
+ export declare function shouldCheckForUpdates(argv?: string[], env?: NodeJS.ProcessEnv, isTty?: boolean): boolean;
14
+ //# sourceMappingURL=update-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClD,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,CAAC;AAsBF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CA+B1E;AA2CD,wBAAsB,eAAe,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA8CrG;AAED,wBAAgB,qBAAqB,CACnC,IAAI,WAAwB,EAC5B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,KAAK,UAAgC,GACpC,OAAO,CAeT"}
@@ -0,0 +1,136 @@
1
+ import { createRequire } from "node:module";
2
+ import { appConfigPath, readJson, writeJson } from "./fs.js";
3
+ const require = createRequire(import.meta.url);
4
+ const packageJson = require("../package.json");
5
+ const PACKAGE_NAME = packageJson.name || "@lightward/mechanic-cli";
6
+ const CURRENT_VERSION = packageJson.version || "0.0.0";
7
+ const NPM_LATEST_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME).replace("%40", "@")}/latest`;
8
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
9
+ const NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1000;
10
+ const FETCH_TIMEOUT_MS = 1500;
11
+ function parseVersion(version) {
12
+ const [core, prerelease] = version.trim().replace(/^v/i, "").split("-", 2);
13
+ const parts = core.split(".");
14
+ if (parts.length < 2 || parts.length > 3 || parts.some((part) => !/^\d+$/.test(part))) {
15
+ return null;
16
+ }
17
+ const numbers = parts.map((part) => Number.parseInt(part, 10));
18
+ return {
19
+ numbers: [
20
+ numbers[0] || 0,
21
+ numbers[1] || 0,
22
+ numbers[2] || 0,
23
+ ],
24
+ prerelease: prerelease || null,
25
+ };
26
+ }
27
+ export function isVersionNewer(candidate, current) {
28
+ const candidateParts = parseVersion(candidate);
29
+ const currentParts = parseVersion(current);
30
+ if (!candidateParts || !currentParts) {
31
+ return false;
32
+ }
33
+ for (const index of [0, 1, 2]) {
34
+ if (candidateParts.numbers[index] > currentParts.numbers[index]) {
35
+ return true;
36
+ }
37
+ if (candidateParts.numbers[index] < currentParts.numbers[index]) {
38
+ return false;
39
+ }
40
+ }
41
+ if (!candidateParts.prerelease && currentParts.prerelease) {
42
+ return true;
43
+ }
44
+ if (candidateParts.prerelease && !currentParts.prerelease) {
45
+ return false;
46
+ }
47
+ return Boolean(candidateParts.prerelease
48
+ && currentParts.prerelease
49
+ && candidateParts.prerelease > currentParts.prerelease);
50
+ }
51
+ function millisecondsSince(value, now) {
52
+ if (!value) {
53
+ return Number.POSITIVE_INFINITY;
54
+ }
55
+ const parsed = Date.parse(value);
56
+ return Number.isFinite(parsed) ? now.getTime() - parsed : Number.POSITIVE_INFINITY;
57
+ }
58
+ async function fetchLatestPackageVersion() {
59
+ const controller = new AbortController();
60
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
61
+ try {
62
+ const response = await fetch(NPM_LATEST_URL, {
63
+ headers: { Accept: "application/json" },
64
+ signal: controller.signal,
65
+ });
66
+ if (!response.ok) {
67
+ return null;
68
+ }
69
+ const body = await response.json();
70
+ return typeof body.version === "string" ? body.version : null;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ finally {
76
+ clearTimeout(timeout);
77
+ }
78
+ }
79
+ async function writeCache(cachePath, cache) {
80
+ try {
81
+ await writeJson(cachePath, cache);
82
+ }
83
+ catch {
84
+ // Update checks are best-effort and must never affect command behavior.
85
+ }
86
+ }
87
+ export async function getUpdateNotice(options = {}) {
88
+ const now = options.now || new Date();
89
+ const currentVersion = options.currentVersion || CURRENT_VERSION;
90
+ const cachePath = options.cachePath || appConfigPath("update-check.json");
91
+ const fetchLatestVersion = options.fetchLatestVersion || fetchLatestPackageVersion;
92
+ const cache = await readJson(cachePath, {}).catch(() => ({}));
93
+ const checkedRecently = millisecondsSince(cache.checked_at, now) < CHECK_INTERVAL_MS;
94
+ let cacheChanged = false;
95
+ let latestVersion = cache.latest_version || null;
96
+ if (!checkedRecently) {
97
+ const fetchedVersion = await fetchLatestVersion();
98
+ cache.checked_at = now.toISOString();
99
+ cacheChanged = true;
100
+ if (fetchedVersion) {
101
+ latestVersion = fetchedVersion;
102
+ cache.latest_version = fetchedVersion;
103
+ }
104
+ }
105
+ if (!latestVersion || !isVersionNewer(latestVersion, currentVersion)) {
106
+ if (cacheChanged) {
107
+ await writeCache(cachePath, cache);
108
+ }
109
+ return null;
110
+ }
111
+ const notifiedRecently = millisecondsSince(cache.last_notified_at, now) < NOTIFY_INTERVAL_MS;
112
+ if (notifiedRecently) {
113
+ if (cacheChanged) {
114
+ await writeCache(cachePath, cache);
115
+ }
116
+ return null;
117
+ }
118
+ cache.last_notified_at = now.toISOString();
119
+ await writeCache(cachePath, cache);
120
+ return {
121
+ currentVersion,
122
+ latestVersion,
123
+ };
124
+ }
125
+ export function shouldCheckForUpdates(argv = process.argv.slice(2), env = process.env, isTty = process.stdout.isTTY === true) {
126
+ if (!isTty) {
127
+ return false;
128
+ }
129
+ if (env.CI
130
+ || env.GITHUB_ACTIONS
131
+ || env.MECHANIC_SKIP_UPDATE_CHECK === "1"
132
+ || env.MECHANIC_UPDATE_CHECK === "0") {
133
+ return false;
134
+ }
135
+ return !argv.some((arg) => arg === "--json" || arg.startsWith("--json="));
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightward/mechanic-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Develop, preview, diff, and publish Mechanic Shopify automation tasks from local files",
5
5
  "type": "module",
6
6
  "keywords": [