@nextcommerce/campaigns-os 1.43.1 → 1.46.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.
Files changed (92) hide show
  1. package/AGENTS.md +9 -2
  2. package/CHANGELOG.md +1099 -5103
  3. package/README.md +34 -13
  4. package/agents/claude/CLAUDE.md +1 -1
  5. package/agents/codex/AGENTS.md +1 -1
  6. package/agents/copilot/copilot-instructions.md +1 -1
  7. package/agents/cursor/campaigns-os.mdc +1 -1
  8. package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +2 -2
  9. package/campaign-spec/dist/rules/analytics-contract-shape.js +2 -2
  10. package/campaign-spec/dist/rules/store-profile-shape.d.ts +5 -1
  11. package/campaign-spec/dist/rules/store-profile-shape.js +8 -10
  12. package/campaign-spec/dist/types.d.ts +2 -2
  13. package/contracts/agent-relevant-change-policy.v1.json +5 -0
  14. package/contracts/archive/CHANGELOG.2026-09-30.md +5111 -0
  15. package/contracts/archive/release-ledger.2026-09-30.json +5068 -0
  16. package/contracts/effects.v1.json +1184 -121
  17. package/contracts/orientation-reason-codes.v1.json +7 -0
  18. package/contracts/release-ledger.json +2190 -5260
  19. package/contracts/supported-surface.json +7 -4
  20. package/contracts/template-slot-manifest.shared-content-core.v0.json +403 -0
  21. package/docs/brand-theme-bridge.md +81 -0
  22. package/docs/build-packet.md +222 -23
  23. package/docs/campaigns-os-build-flow.md +4 -3
  24. package/docs/design-source-package.md +162 -15
  25. package/docs/effects.md +66 -12
  26. package/docs/gateway-login.md +3 -0
  27. package/docs/local-setup.md +1 -1
  28. package/docs/orientation-contract-reference.md +42 -2
  29. package/docs/polish-evidence.md +74 -0
  30. package/docs/progress-snapshots.md +10 -6
  31. package/docs/qa-and-test-orders.md +230 -20
  32. package/docs/release-ledger-authoring-guide.md +70 -8
  33. package/docs/runtime-readiness.md +1 -1
  34. package/docs/sdk-storage-compatibility.md +1 -1
  35. package/docs/skills-revision.md +10 -10
  36. package/docs/supported-surface.md +2 -2
  37. package/docs/versioning.md +4 -1
  38. package/package.json +1 -1
  39. package/schemas/campaigns-os-release-ledger.v1.schema.json +32 -2
  40. package/schemas/campaigns-os-tooling-orientation.v1.schema.json +1 -0
  41. package/skills/campaign-lifecycle-orientation/SKILL.md +16 -5
  42. package/skills/campaign-readback-classification/SKILL.md +3 -3
  43. package/skills/campaign-run-evidence/SKILL.md +7 -6
  44. package/skills/contribution-intake/SKILL.md +3 -3
  45. package/skills/next-campaigns-build/SKILL.md +7 -6
  46. package/skills/next-campaigns-os/SKILL.md +7 -7
  47. package/skills/next-campaigns-os/references/session-intake.md +9 -3
  48. package/skills/next-campaigns-os-setup/SKILL.md +5 -5
  49. package/skills/next-campaigns-polish/SKILL.md +28 -9
  50. package/skills/next-campaigns-qa/SKILL.md +7 -4
  51. package/skills.json +10 -10
  52. package/src/brand-theme.mjs +320 -20
  53. package/src/build-brief.mjs +6 -4
  54. package/src/built-script-syntax.mjs +480 -0
  55. package/src/built-site-scope.mjs +16 -4
  56. package/src/campaigns-api-key.mjs +99 -0
  57. package/src/cli-helpers.mjs +118 -0
  58. package/src/cli.mjs +1530 -7580
  59. package/src/commercial-parity.mjs +48 -2
  60. package/src/design-source-package.mjs +1 -1
  61. package/src/design-source-publication.mjs +898 -0
  62. package/src/deviation.mjs +13 -1
  63. package/src/diagnostic.mjs +6 -2
  64. package/src/directory-lock.mjs +270 -0
  65. package/src/doctor/checks.mjs +4654 -0
  66. package/src/doctor/inspect.mjs +678 -0
  67. package/src/doctor/next-step.mjs +731 -0
  68. package/src/doctor/source-provenance.mjs +184 -0
  69. package/src/install-invocation.mjs +29 -0
  70. package/src/invocation.mjs +183 -0
  71. package/src/live-campaign-refs.mjs +466 -0
  72. package/src/login.mjs +2 -2
  73. package/src/page-kit-store-profile.mjs +69 -12
  74. package/src/page-kit-sync.mjs +31 -12
  75. package/src/private-template-source.mjs +1 -1
  76. package/src/progress-node.mjs +9 -36
  77. package/src/proof-policy.mjs +1 -1
  78. package/src/qa-analytics-correctness.mjs +3 -0
  79. package/src/qa-binding-evidence.mjs +76 -11
  80. package/src/qa-browser.mjs +1316 -105
  81. package/src/qa-build-scope.mjs +47 -0
  82. package/src/qa-commercial-parity.mjs +48 -5
  83. package/src/qa-node.mjs +339 -19
  84. package/src/qa-test-order-topology.mjs +148 -0
  85. package/src/sdk-markup.mjs +72 -8
  86. package/src/source-html-intake.mjs +117 -1
  87. package/src/source-html-manifest.mjs +9 -2
  88. package/src/stage-ledger.mjs +28 -0
  89. package/src/stage-record.mjs +551 -0
  90. package/src/target-lock.mjs +54 -0
  91. package/src/template-brand-contract.mjs +17 -1
  92. package/src/upsell-selector-scope.mjs +112 -2
@@ -0,0 +1,118 @@
1
+ // Small general helpers the CLI and the doctor modules share.
2
+ import { htmlScanDigest } from "./html-scan.mjs";
3
+ import { existsSync, readFileSync, statSync } from "node:fs";
4
+ import { dirname, isAbsolute, relative, resolve } from "node:path";
5
+ import { canonicalPath } from "./fs-identity.mjs";
6
+ import { refused } from "./lifecycle.mjs";
7
+ import { isAbsoluteHttpUrl } from "./route-identity.mjs";
8
+
9
+ // The shared "missing required flag" refusal. Every call site resolves flags
10
+ // at the top of its handler, before the command reads or writes anything, so
11
+ // this is always an up-front refusal and carries the tag.
12
+ function requireArg(args, key) {
13
+ const value = args[key];
14
+ if (!isNonEmptyString(value)) throw refused(`Missing required --${key}`);
15
+ return value;
16
+ }
17
+
18
+ function isObject(value) {
19
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+
22
+ function isNonEmptyString(value) {
23
+ return typeof value === "string" && value.trim().length > 0;
24
+ }
25
+
26
+ function optionalString(value, fallback = null) {
27
+ return isNonEmptyString(value) ? value.trim() : fallback;
28
+ }
29
+
30
+ function firstNonEmptyString(...values) {
31
+ for (const value of values) {
32
+ if (isNonEmptyString(value)) return value.trim();
33
+ }
34
+ return null;
35
+ }
36
+
37
+ function cloneJson(value) {
38
+ return JSON.parse(JSON.stringify(value));
39
+ }
40
+
41
+ function filesystemPathsMatch(left, right) {
42
+ if (!isNonEmptyString(left) || !isNonEmptyString(right)) return false;
43
+ if (isAbsoluteHttpUrl(left) || isAbsoluteHttpUrl(right)) return left === right;
44
+ const resolvedLeft = resolve(left);
45
+ const resolvedRight = resolve(right);
46
+ if (resolvedLeft === resolvedRight) return true;
47
+ try {
48
+ const leftStats = statSync(resolvedLeft);
49
+ const rightStats = statSync(resolvedRight);
50
+ return leftStats.dev === rightStats.dev && leftStats.ino === rightStats.ino;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ function readJson(path) {
57
+ return JSON.parse(readFileSync(path, "utf8"));
58
+ }
59
+
60
+ function readJsonIfExists(path) {
61
+ return path && existsSync(path) ? readJson(path) : null;
62
+ }
63
+
64
+ function sha256File(path) {
65
+ return htmlScanDigest(path);
66
+ }
67
+
68
+ // Portable output rebases every path onto the output base as the filesystem
69
+ // knows both (real paths when they exist), so a target reached through a
70
+ // symlinked checkout and a sidecar recorded by its real path relativize to
71
+ // `./…`, not to a chain of `../` that names the original machine.
72
+ function relFromDir(dirPath, targetPath) {
73
+ const rel = relative(canonicalPath(dirPath), canonicalPath(targetPath));
74
+ if (!rel) return ".";
75
+ return rel.startsWith(".") ? rel : `./${rel}`;
76
+ }
77
+
78
+ function isLocalAbsolutePath(value) {
79
+ return isNonEmptyString(value) && !isAbsoluteHttpUrl(value) && isAbsolute(value);
80
+ }
81
+
82
+ function resolveFromFile(filePath, targetPath) {
83
+ if (!isNonEmptyString(targetPath)) return null;
84
+ if (isAbsoluteHttpUrl(targetPath)) return targetPath;
85
+ return resolve(dirname(resolve(filePath)), targetPath);
86
+ }
87
+
88
+ function extractFrontmatterValue(content, key) {
89
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
90
+ if (!match) return null;
91
+ for (const line of match[1].split("\n")) {
92
+ const parts = line.match(/^([A-Za-z0-9_-]+):\s*(.+?)\s*$/);
93
+ if (parts?.[1] === key) return parts[2].replace(/^["']|["']$/g, "");
94
+ }
95
+ return null;
96
+ }
97
+
98
+ function addIssue(collection, code, message, detail = null) {
99
+ collection.push(detail ? { code, message, detail } : { code, message });
100
+ }
101
+
102
+ export {
103
+ requireArg,
104
+ isObject,
105
+ isNonEmptyString,
106
+ optionalString,
107
+ firstNonEmptyString,
108
+ cloneJson,
109
+ readJson,
110
+ readJsonIfExists,
111
+ sha256File,
112
+ relFromDir,
113
+ isLocalAbsolutePath,
114
+ resolveFromFile,
115
+ filesystemPathsMatch,
116
+ extractFrontmatterValue,
117
+ addIssue,
118
+ };