@popoverai/dotrequirements 0.33.0 → 0.34.1

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 (119) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +13 -1
  3. package/dist/cloud.d.ts +5 -1
  4. package/dist/cloud.js +6 -2
  5. package/dist/commands/acceptance-test.js +1 -1
  6. package/dist/commands/board/index.d.ts +13 -0
  7. package/dist/commands/board/index.js +96 -0
  8. package/dist/commands/branch-merge.d.ts +78 -0
  9. package/dist/commands/branch-merge.js +129 -0
  10. package/dist/commands/diff.js +15 -5
  11. package/dist/commands/mockup/index.d.ts +11 -0
  12. package/dist/commands/mockup/index.js +82 -0
  13. package/dist/commands/scores/index.d.ts +50 -0
  14. package/dist/commands/scores/index.js +190 -0
  15. package/dist/commands/solution/ask.d.ts +9 -0
  16. package/dist/commands/solution/ask.js +27 -0
  17. package/dist/commands/solution/author.d.ts +19 -0
  18. package/dist/commands/solution/author.js +202 -0
  19. package/dist/commands/solution/branch.d.ts +103 -0
  20. package/dist/commands/solution/branch.js +341 -0
  21. package/dist/commands/solution/brief.d.ts +81 -0
  22. package/dist/commands/solution/brief.js +47 -0
  23. package/dist/commands/solution/checkout.d.ts +27 -0
  24. package/dist/commands/solution/checkout.js +66 -0
  25. package/dist/commands/solution/client.d.ts +16 -0
  26. package/dist/commands/solution/client.js +46 -0
  27. package/dist/commands/solution/contract.d.ts +9 -0
  28. package/dist/commands/solution/contract.js +23 -0
  29. package/dist/commands/solution/create.d.ts +14 -0
  30. package/dist/commands/solution/create.js +52 -0
  31. package/dist/commands/solution/demo-script.d.ts +28 -0
  32. package/dist/commands/solution/demo-script.js +43 -0
  33. package/dist/commands/solution/image.d.ts +60 -0
  34. package/dist/commands/solution/image.js +148 -0
  35. package/dist/commands/solution/index.d.ts +12 -0
  36. package/dist/commands/solution/index.js +282 -0
  37. package/dist/commands/solution/look-up.d.ts +9 -0
  38. package/dist/commands/solution/look-up.js +69 -0
  39. package/dist/commands/solution/next.d.ts +12 -0
  40. package/dist/commands/solution/next.js +48 -0
  41. package/dist/commands/solution/plan-file.d.ts +22 -0
  42. package/dist/commands/solution/plan-file.js +33 -0
  43. package/dist/commands/solution/preview-screenshot.d.ts +21 -0
  44. package/dist/commands/solution/preview-screenshot.js +56 -0
  45. package/dist/commands/solution/pull-request.d.ts +25 -0
  46. package/dist/commands/solution/pull-request.js +117 -0
  47. package/dist/commands/solution/pushing.d.ts +66 -0
  48. package/dist/commands/solution/pushing.js +185 -0
  49. package/dist/commands/solution/rehearse.d.ts +10 -0
  50. package/dist/commands/solution/rehearse.js +24 -0
  51. package/dist/commands/solution/request.d.ts +3 -0
  52. package/dist/commands/solution/request.js +13 -0
  53. package/dist/commands/solution/review.d.ts +27 -0
  54. package/dist/commands/solution/review.js +104 -0
  55. package/dist/commands/solution/run-file.d.ts +43 -0
  56. package/dist/commands/solution/run-file.js +69 -0
  57. package/dist/commands/solution/share.d.ts +62 -0
  58. package/dist/commands/solution/share.js +346 -0
  59. package/dist/commands/solution/stall.d.ts +14 -0
  60. package/dist/commands/solution/stall.js +41 -0
  61. package/dist/commands/solution/start.d.ts +14 -0
  62. package/dist/commands/solution/start.js +31 -0
  63. package/dist/commands/solution/status.d.ts +39 -0
  64. package/dist/commands/solution/status.js +145 -0
  65. package/dist/commands/solution/summary.d.ts +13 -0
  66. package/dist/commands/solution/summary.js +5 -0
  67. package/dist/commands/solution/things.d.ts +118 -0
  68. package/dist/commands/solution/things.js +78 -0
  69. package/dist/commands/solution/thread.d.ts +13 -0
  70. package/dist/commands/solution/thread.js +53 -0
  71. package/dist/commands/solution/upload.d.ts +24 -0
  72. package/dist/commands/solution/upload.js +27 -0
  73. package/dist/commands/solution/video.d.ts +88 -0
  74. package/dist/commands/solution/video.js +364 -0
  75. package/dist/commands/solution/working-folder.d.ts +44 -0
  76. package/dist/commands/solution/working-folder.js +64 -0
  77. package/dist/commands/sync.d.ts +2 -1
  78. package/dist/commands/sync.js +21 -5
  79. package/dist/convex.d.ts +54 -0
  80. package/dist/convex.js +64 -0
  81. package/dist/push/core.js +7 -2
  82. package/dist/rehearsal/derive.d.ts +23 -0
  83. package/dist/rehearsal/derive.js +112 -0
  84. package/dist/rehearsal/driver.d.ts +44 -0
  85. package/dist/rehearsal/driver.js +337 -0
  86. package/dist/rehearsal/earlier.d.ts +25 -0
  87. package/dist/rehearsal/earlier.js +138 -0
  88. package/dist/rehearsal/index.d.ts +5 -0
  89. package/dist/rehearsal/index.js +4 -0
  90. package/dist/rehearsal/live.d.ts +72 -0
  91. package/dist/rehearsal/live.js +301 -0
  92. package/dist/rehearsal/report.d.ts +4 -0
  93. package/dist/rehearsal/report.js +69 -0
  94. package/dist/rehearsal/types.d.ts +377 -0
  95. package/dist/rehearsal/types.js +8 -0
  96. package/dist/requirements/index.d.ts +6 -0
  97. package/dist/requirements/index.js +24 -0
  98. package/dist/schema/browser.d.ts +2 -1
  99. package/dist/schema/browser.js +6 -1
  100. package/dist/schema/file-writer.d.ts +5 -0
  101. package/dist/schema/file-writer.js +4 -0
  102. package/dist/schema/parser.d.ts +11 -2
  103. package/dist/schema/parser.js +11 -7
  104. package/dist/schema/provenance.d.ts +27 -0
  105. package/dist/schema/provenance.js +66 -0
  106. package/dist/schema/schemas.d.ts +40 -0
  107. package/dist/schema/schemas.js +4 -0
  108. package/dist/sync/execute.d.ts +10 -1
  109. package/dist/sync/execute.js +68 -5
  110. package/dist/sync/index.d.ts +1 -1
  111. package/dist/sync/index.js +1 -1
  112. package/dist/sync/publish.d.ts +7 -0
  113. package/dist/sync/segment.js +4 -1
  114. package/dist/sync/snapshot.d.ts +23 -1
  115. package/dist/sync/snapshot.js +75 -3
  116. package/dist/sync/solution-documents.d.ts +74 -0
  117. package/dist/sync/solution-documents.js +135 -0
  118. package/dist/sync/types.d.ts +20 -0
  119. package/package.json +3 -2
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Where a solution's documents live, the sync's half (SOLDOC; the decision
3
+ * is in docs/working/four-areas-prototype.md, "Where a solution's documents
4
+ * live"). A document that belongs to a solution and is not a requirements
5
+ * document lives in a solution directory, `.requirements/solutions/<solution>/`,
6
+ * named for its outcome; the `.requirements.md` name is what makes a file a
7
+ * requirements document, wherever it sits. A new file declares its belonging
8
+ * by its path; once the frontmatter carries it, the frontmatter is the truth.
9
+ */
10
+ import * as path from "node:path";
11
+ import { sanitizeFileName } from "./local-files.js";
12
+ export const SOLUTIONS_DIR = "solutions";
13
+ /** The solution directory a file sits directly in, if any (SOLDOC-8.1). */
14
+ export function solutionDirOf(filePath) {
15
+ const parts = filePath.split(/[\\/]/);
16
+ const fileName = parts.at(-1);
17
+ const solutionSlug = parts.at(-2);
18
+ if (!fileName ||
19
+ !solutionSlug ||
20
+ parts.at(-3) !== SOLUTIONS_DIR ||
21
+ parts.at(-4) !== ".requirements" ||
22
+ !fileName.endsWith(".md")) {
23
+ return undefined;
24
+ }
25
+ return { solutionSlug, outcomeSlug: stemOf(fileName) };
26
+ }
27
+ /** `pitch` for `pitch.md` and for `pitch.requirements.md`. */
28
+ export function stemOf(fileName) {
29
+ return fileName.replace(/\.requirements\.md$/, "").replace(/\.md$/, "");
30
+ }
31
+ /** SOLDOC-3: the name, never the content. Undefined for a file the rule doesn't reach. */
32
+ export function requirementsDocByName(filePath) {
33
+ if (!solutionDirOf(filePath))
34
+ return undefined;
35
+ return filePath.endsWith(".requirements.md");
36
+ }
37
+ /** The directory a solution's documents live in, by name. */
38
+ export function directoryOf(solution) {
39
+ return solution.directory ?? sanitizeFileName(solution.title);
40
+ }
41
+ /**
42
+ * What a pushed file belongs to (SOLDOC-4, SOLDOC-5): the frontmatter's
43
+ * belonging when it has one, else what its path claims, resolved against
44
+ * the project's solutions. A frontmatter that disagrees with the path is
45
+ * kept and reported; a path that resolves to nothing refuses the push.
46
+ */
47
+ export function resolveBelonging(filePath, frontmatter, claim, solutions) {
48
+ const declared = frontmatter.solution && frontmatter.outcome
49
+ ? { solutionId: frontmatter.solution, outcome: frontmatter.outcome }
50
+ : undefined;
51
+ if (!claim)
52
+ return { belonging: declared };
53
+ const name = path.basename(filePath);
54
+ if (declared) {
55
+ // SOLDOC-5: the frontmatter wins; the path is layout. Say when they part.
56
+ const owner = solutions?.find((s) => s.solutionId === declared.solutionId);
57
+ if (owner && directoryOf(owner) !== claim.solutionSlug) {
58
+ return {
59
+ belonging: declared,
60
+ warning: `${name}: its path names a different solution than its frontmatter ("${owner.title}"); the frontmatter's belonging is kept.`,
61
+ };
62
+ }
63
+ // Read against the solution's other outcomes too: `spec-2` names Spec 2,
64
+ // not the Spec, when the plan has both.
65
+ if (outcomeNamedBy(claim.outcomeSlug, [
66
+ ...(owner?.outcomes ?? []),
67
+ declared.outcome,
68
+ ]) !== declared.outcome) {
69
+ return {
70
+ belonging: declared,
71
+ warning: `${name}: its filename names a different outcome than its frontmatter ("${declared.outcome}"); the frontmatter's belonging is kept.`,
72
+ };
73
+ }
74
+ return { belonging: declared };
75
+ }
76
+ // SOLDOC-4, SOLDOC-9: a new file's path is its declaration, read against
77
+ // each solution's own directory name.
78
+ const matches = (solutions ?? []).filter((s) => directoryOf(s) === claim.solutionSlug);
79
+ if (matches.length === 0) {
80
+ throw new Error(`${name}: its directory "${claim.solutionSlug}" matches no solution in the project, so it can't be placed. Name the directory as the solution's is named: its title as a slug, with its numeric suffix when an older solution has the same title.`);
81
+ }
82
+ if (matches.length > 1) {
83
+ throw new Error(`${name}: its directory "${claim.solutionSlug}" matches more than one solution (${matches.map((s) => `"${s.title}"`).join(", ")}), so it can't be placed.`);
84
+ }
85
+ const [solution] = matches;
86
+ const outcome = outcomeNamedBy(claim.outcomeSlug, solution.outcomes);
87
+ if (!outcome) {
88
+ throw new Error(`${name}: "${claim.outcomeSlug}" matches no outcome of "${solution.title}". Its plan produces: ${solution.outcomes.map((o) => `${o} (${sanitizeFileName(o)}.md)`).join(", ")}.`);
89
+ }
90
+ return { belonging: { solutionId: solution.solutionId, outcome } };
91
+ }
92
+ /**
93
+ * SOLDOC-4, SOLDOC-19: the outcome a file's stem names, of those given: the
94
+ * one whose slug the stem is, or else the one whose slug it is followed by a
95
+ * suffix from -2 up, as an outcome's later documents are named. So `spec-2`
96
+ * is Spec 2's when there is a Spec 2, and the Spec's otherwise.
97
+ */
98
+ export function outcomeNamedBy(stem, outcomes) {
99
+ const plain = outcomes.find((o) => sanitizeFileName(o) === stem);
100
+ if (plain !== undefined)
101
+ return plain;
102
+ const later = /^(.+)-([1-9]\d*)$/.exec(stem);
103
+ if (!later || Number(later[2]) < 2)
104
+ return undefined;
105
+ return outcomes.find((o) => sanitizeFileName(o) === later[1]);
106
+ }
107
+ /**
108
+ * SOLDOC-6, SOLDOC-9, SOLDOC-19: where a pulled document that belongs to a
109
+ * solution lands when it has no file yet: in the solution's own directory,
110
+ * at the file name the backend gives it, which counts the outcome's other
111
+ * documents. A backend from before it was sent leaves it out, and the
112
+ * outcome's plain name stands in: its slug, with the `.requirements.md` name
113
+ * when it is a requirements document. When that file is taken, the document
114
+ * takes the outcome's next free name from -2 up.
115
+ */
116
+ export function solutionDocumentPath(requirementsDir, solutionDirectory, outcome, isRequirementsDoc, fileName, taken = () => false) {
117
+ const suffix = isRequirementsDoc ? ".requirements.md" : ".md";
118
+ const directory = path.join(requirementsDir, SOLUTIONS_DIR, solutionDirectory || "solution");
119
+ const slug = sanitizeFileName(outcome) || "outcome";
120
+ // Only a plain file name of the document's kind, never a path.
121
+ const given = fileName &&
122
+ fileName === path.basename(fileName) &&
123
+ !fileName.startsWith(".") &&
124
+ stemOf(fileName) !== "" &&
125
+ fileName.endsWith(suffix) &&
126
+ (isRequirementsDoc || !fileName.endsWith(".requirements.md"))
127
+ ? fileName
128
+ : `${slug}${suffix}`;
129
+ let candidate = path.join(directory, given);
130
+ for (let n = 2; taken(candidate); n++) {
131
+ candidate = path.join(directory, `${slug}-${n}${suffix}`);
132
+ }
133
+ return candidate;
134
+ }
135
+ //# sourceMappingURL=solution-documents.js.map
@@ -29,6 +29,9 @@ export interface LocalDocument {
29
29
  defaultPrefix?: string;
30
30
  /** document.attachments from frontmatter (ATTACH-10). */
31
31
  attachments?: Attachment[];
32
+ /** SOLDOC-5: the belonging the frontmatter declares. */
33
+ solution?: string;
34
+ outcome?: string;
32
35
  /** Frontmatter-stripped markdown body (the comparison unit, SYNC-ARCH-1). */
33
36
  body?: string;
34
37
  parseError?: string;
@@ -46,6 +49,18 @@ export interface CloudDocument {
46
49
  body: string;
47
50
  version: number;
48
51
  updatedAt: number;
52
+ /** SOLDOC-6: which solution and outcome it belongs to, with the solution's title and directory name. */
53
+ solution?: {
54
+ solutionId: string;
55
+ outcome: string;
56
+ title: string | null;
57
+ /** SOLDOC-9: the solution's directory name; absent from an older backend. */
58
+ directory?: string | null;
59
+ /** SOLDOC-19: the document's file name in that directory; absent from an older backend. */
60
+ fileName?: string | null;
61
+ };
62
+ /** SOLDOC-2/3: absent means a requirements document. */
63
+ isRequirementsDoc?: boolean;
49
64
  }
50
65
  export interface LocalSnapshot {
51
66
  documents: LocalDocument[];
@@ -54,6 +69,11 @@ export interface CloudSnapshot {
54
69
  projectName: string;
55
70
  projectSlug: string;
56
71
  documents: CloudDocument[];
72
+ /**
73
+ * SOLACCESS-2.2: whether the project's team has solutions, as the export
74
+ * read it; absent from an older backend, which reads as no.
75
+ */
76
+ solutions?: boolean;
57
77
  }
58
78
  /**
59
79
  * A single point of difference within a paired document, for verbose
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popoverai/dotrequirements",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "Requirements as testable, human-readable data — CLI and test harness for spec-driven development with AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,8 @@
13
13
  "./schema": "./dist/schema/index.js",
14
14
  "./schema/browser": "./dist/schema/browser.js",
15
15
  "./style-guide": "./dist/requirements/style-guide.js",
16
- "./greenfield": "./dist/requirements/greenfield.js"
16
+ "./greenfield": "./dist/requirements/greenfield.js",
17
+ "./rehearsal": "./dist/rehearsal/index.js"
17
18
  },
18
19
  "publishConfig": {
19
20
  "access": "public"