superdev-cli 0.1.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 (182) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +21 -0
  3. package/.codex-plugin/plugin.json +27 -0
  4. package/CODE_OF_CONDUCT.md +109 -0
  5. package/CONTRIBUTING.md +205 -0
  6. package/LICENSE +202 -0
  7. package/NOTICE +11 -0
  8. package/README.md +1051 -0
  9. package/SECURITY.md +135 -0
  10. package/THIRD-PARTY-NOTICES.md +253 -0
  11. package/hooks/hooks.json +61 -0
  12. package/hooks/run.mjs +88 -0
  13. package/package.json +65 -0
  14. package/references/confidentiality.md +48 -0
  15. package/references/evidence-and-risk.md +73 -0
  16. package/references/operating-model.md +105 -0
  17. package/references/platform-capabilities.md +51 -0
  18. package/references/project-record.md +91 -0
  19. package/references/provider-contracts.md +102 -0
  20. package/scripts/doctor/doctor.mjs +539 -0
  21. package/scripts/privacy/scan-history.mjs +163 -0
  22. package/scripts/privacy/scan.mjs +368 -0
  23. package/scripts/validate/README.md +94 -0
  24. package/scripts/validate/common.mjs +149 -0
  25. package/scripts/validate/data-model.mjs +225 -0
  26. package/scripts/validate/dependencies.mjs +69 -0
  27. package/scripts/validate/docs-templates.mjs +99 -0
  28. package/scripts/validate/footprint.mjs +78 -0
  29. package/scripts/validate/imports.mjs +61 -0
  30. package/scripts/validate/manifests.mjs +173 -0
  31. package/scripts/validate/markdown.mjs +123 -0
  32. package/scripts/validate/migrations.mjs +187 -0
  33. package/scripts/validate/no-tests.mjs +136 -0
  34. package/scripts/validate/privacy.mjs +115 -0
  35. package/scripts/validate/record-links.mjs +127 -0
  36. package/scripts/validate/skill-commands.mjs +190 -0
  37. package/scripts/validate/skills.mjs +112 -0
  38. package/scripts/validate/specification.mjs +88 -0
  39. package/scripts/validate/style.mjs +78 -0
  40. package/scripts/validate/validate-all.mjs +175 -0
  41. package/skills/debug/SKILL.md +68 -0
  42. package/skills/decision/SKILL.md +99 -0
  43. package/skills/docs/SKILL.md +86 -0
  44. package/skills/docs/assets/fragments/api/events.md +14 -0
  45. package/skills/docs/assets/fragments/api/graphql.md +14 -0
  46. package/skills/docs/assets/fragments/api/local-only.md +13 -0
  47. package/skills/docs/assets/fragments/api/rest.md +13 -0
  48. package/skills/docs/assets/fragments/api/rpc.md +13 -0
  49. package/skills/docs/assets/fragments/async/event-bus.md +13 -0
  50. package/skills/docs/assets/fragments/async/none.md +11 -0
  51. package/skills/docs/assets/fragments/async/platform-jobs.md +13 -0
  52. package/skills/docs/assets/fragments/async/queue.md +13 -0
  53. package/skills/docs/assets/fragments/async/scheduler.md +13 -0
  54. package/skills/docs/assets/fragments/auth/detected-provider.md +15 -0
  55. package/skills/docs/assets/fragments/auth/neutral-base.md +14 -0
  56. package/skills/docs/assets/fragments/data/document.md +13 -0
  57. package/skills/docs/assets/fragments/data/external-saas.md +13 -0
  58. package/skills/docs/assets/fragments/data/key-value.md +13 -0
  59. package/skills/docs/assets/fragments/data/none.md +12 -0
  60. package/skills/docs/assets/fragments/data/sql-orm.md +13 -0
  61. package/skills/docs/assets/fragments/data/sql-plain.md +13 -0
  62. package/skills/docs/assets/fragments/env/conventional.md +13 -0
  63. package/skills/docs/assets/fragments/env/envx.md +13 -0
  64. package/skills/docs/assets/fragments/env/managed-platform.md +13 -0
  65. package/skills/docs/assets/fragments/env/none.md +11 -0
  66. package/skills/docs/assets/fragments/ui/api-only.md +13 -0
  67. package/skills/docs/assets/fragments/ui/cli.md +14 -0
  68. package/skills/docs/assets/fragments/ui/desktop.md +14 -0
  69. package/skills/docs/assets/fragments/ui/mobile.md +14 -0
  70. package/skills/docs/assets/fragments/ui/web.md +14 -0
  71. package/skills/docs/assets/templates/adr.md +62 -0
  72. package/skills/docs/assets/templates/api.md +30 -0
  73. package/skills/docs/assets/templates/architecture.md +52 -0
  74. package/skills/docs/assets/templates/change-impact-drift-report.md +29 -0
  75. package/skills/docs/assets/templates/compliance.md +29 -0
  76. package/skills/docs/assets/templates/data-schema.md +43 -0
  77. package/skills/docs/assets/templates/feature.md +43 -0
  78. package/skills/docs/assets/templates/foundations.md +55 -0
  79. package/skills/docs/assets/templates/jobs-webhooks.md +36 -0
  80. package/skills/docs/assets/templates/module-inventory.md +19 -0
  81. package/skills/docs/assets/templates/module.md +50 -0
  82. package/skills/docs/assets/templates/nfr.md +22 -0
  83. package/skills/docs/assets/templates/observability.md +28 -0
  84. package/skills/docs/assets/templates/pages-ui-actions.md +47 -0
  85. package/skills/docs/assets/templates/project-summary.md +44 -0
  86. package/skills/docs/assets/templates/roles-permissions.md +26 -0
  87. package/skills/docs/assets/templates/test-plan.md +23 -0
  88. package/skills/docs/assets/templates/workflow-state-machine.md +43 -0
  89. package/skills/docs/references/adr-authoring.md +24 -0
  90. package/skills/docs/references/apis-and-data.md +23 -0
  91. package/skills/docs/references/capability-fragments.md +25 -0
  92. package/skills/docs/references/change-tracking.md +62 -0
  93. package/skills/docs/references/diagrams.md +31 -0
  94. package/skills/docs/references/discovery.md +59 -0
  95. package/skills/docs/references/edge-cases.md +45 -0
  96. package/skills/docs/references/foundations-modules.md +18 -0
  97. package/skills/docs/references/ingestion.md +52 -0
  98. package/skills/docs/references/initialize-adopt.md +25 -0
  99. package/skills/docs/references/module-decomposition.md +38 -0
  100. package/skills/docs/references/profiles.md +60 -0
  101. package/skills/docs/references/quality-attributes.md +27 -0
  102. package/skills/docs/references/reverse-engineer.md +21 -0
  103. package/skills/docs/references/spec-depths.md +31 -0
  104. package/skills/docs/references/summarize.md +18 -0
  105. package/skills/docs/references/surfaces-and-actions.md +24 -0
  106. package/skills/docs/references/validation.md +47 -0
  107. package/skills/docs/references/workflows-and-jobs.md +25 -0
  108. package/skills/docs/scripts/ingest.mjs +984 -0
  109. package/skills/docs/scripts/profile-detect.mjs +299 -0
  110. package/skills/docs/scripts/screen.mjs +96 -0
  111. package/skills/docs/scripts/template-lint.mjs +143 -0
  112. package/skills/docs/scripts/validate-docs.mjs +363 -0
  113. package/skills/doctor/SKILL.md +167 -0
  114. package/skills/feature/SKILL.md +132 -0
  115. package/skills/init/SKILL.md +137 -0
  116. package/skills/project/SKILL.md +261 -0
  117. package/skills/project/references/commands.md +213 -0
  118. package/skills/resume/SKILL.md +79 -0
  119. package/skills/review/SKILL.md +91 -0
  120. package/skills/status/SKILL.md +85 -0
  121. package/skills/task/SKILL.md +183 -0
  122. package/src/cli/product-map.mjs +468 -0
  123. package/src/cli/render.mjs +544 -0
  124. package/src/cli.mjs +2774 -0
  125. package/src/cloud/crypto.mjs +118 -0
  126. package/src/cloud/merge.mjs +186 -0
  127. package/src/cloud/policy.mjs +115 -0
  128. package/src/cloud/sync.mjs +512 -0
  129. package/src/cloud/transport.mjs +116 -0
  130. package/src/db/connect.mjs +189 -0
  131. package/src/db/maintenance.mjs +419 -0
  132. package/src/db/migrate.mjs +188 -0
  133. package/src/db/migrations/001_initial.sql +1024 -0
  134. package/src/db/migrations/002_docs_coverage.sql +126 -0
  135. package/src/db/migrations/003_memory_retrieval_and_integrity.sql +168 -0
  136. package/src/db/migrations/004_task_categories.sql +59 -0
  137. package/src/db/migrations/005_executable_evidence.sql +32 -0
  138. package/src/db/migrations/006_module_and_feature_boundaries.sql +27 -0
  139. package/src/db/migrations/007_drop_redundant_module_users.sql +13 -0
  140. package/src/db/migrations/008_changes_assumptions_test_plans_api_services.sql +166 -0
  141. package/src/db/migrations/009_retired_documents.sql +54 -0
  142. package/src/db/migrations/010_test_plan_evidence.sql +20 -0
  143. package/src/db/migrations/011_criterion_waiver.sql +13 -0
  144. package/src/db/migrations/012_synchronization.sql +56 -0
  145. package/src/db/store.mjs +327 -0
  146. package/src/decisions/record.mjs +167 -0
  147. package/src/docs/proposals.mjs +871 -0
  148. package/src/docs/render.mjs +1424 -0
  149. package/src/docs/templates.mjs +1281 -0
  150. package/src/features/acceptance.mjs +315 -0
  151. package/src/features/specify.mjs +248 -0
  152. package/src/init/discovery.mjs +901 -0
  153. package/src/init/index.mjs +784 -0
  154. package/src/init/questions.mjs +562 -0
  155. package/src/memory/benchmark.mjs +171 -0
  156. package/src/memory/capture.mjs +135 -0
  157. package/src/memory/consolidate.mjs +255 -0
  158. package/src/memory/index.mjs +810 -0
  159. package/src/model/ids.mjs +185 -0
  160. package/src/model/screening.mjs +150 -0
  161. package/src/model/toolkit.mjs +258 -0
  162. package/src/model/vocabulary.mjs +190 -0
  163. package/src/product/assumptions.mjs +120 -0
  164. package/src/product/changes.mjs +180 -0
  165. package/src/product/test-plans.mjs +183 -0
  166. package/src/progress/index.mjs +1364 -0
  167. package/src/runtime/harness.mjs +264 -0
  168. package/src/runtime/hooks.mjs +1021 -0
  169. package/src/runtime/identity.mjs +305 -0
  170. package/src/runtime/resume.mjs +343 -0
  171. package/src/runtime/session.mjs +679 -0
  172. package/src/runtime/version.mjs +315 -0
  173. package/src/service/assets/control-center.html +206 -0
  174. package/src/service/assets/control-center.manifest.json +7 -0
  175. package/src/service/manage.mjs +542 -0
  176. package/src/service/mutations.mjs +860 -0
  177. package/src/service/read-model.mjs +1557 -0
  178. package/src/service/server.mjs +783 -0
  179. package/src/tasks/categories.mjs +154 -0
  180. package/src/tasks/derive.mjs +977 -0
  181. package/src/tasks/lifecycle.mjs +830 -0
  182. package/src/verify/index.mjs +236 -0
@@ -0,0 +1,236 @@
1
+ // Re-run the checks that evidence stands on.
2
+ //
3
+ // Completion is gated on evidence, and evidence was a sentence written once and
4
+ // believed forever. Nothing re-read it, so a task completed in January still
5
+ // claimed to be proven in June by a check that had stopped passing in March.
6
+ // The schema always expected better: verification_evidence has carried status,
7
+ // stale_at and content_hash from the first migration, and nothing ever set them.
8
+ //
9
+ // So evidence carries the command that proved it, and this re-runs them. A
10
+ // claim that no longer holds is marked stale and reported. Whether that reopens
11
+ // the task is a separate decision: reporting first, and gating once the backlog
12
+ // is clean, is the order that does not disrupt work in flight.
13
+ //
14
+ // Not every verification has a command. Reading a screenshot, confirming a
15
+ // decision with the owner, watching a migration take a backup: requiring a
16
+ // command for those would only encourage inventing one, which is worse than
17
+ // saying plainly that a check is manual.
18
+
19
+ import { execFile } from "node:child_process";
20
+ import { promisify } from "node:util";
21
+
22
+ import { mutate, query } from "../db/store.mjs";
23
+
24
+ const run = promisify(execFile);
25
+
26
+ /**
27
+ * What may run unattended, enumerated by exact shape.
28
+ *
29
+ * A recorded command is text that arrived from somewhere, most often from an
30
+ * agent, and it is executed on the machine of whoever runs verify. An earlier
31
+ * version of this allowed a list of programs and rejected a list of dangerous
32
+ * substrings. That is the wrong way round, and a security review was right to
33
+ * say so: a program allowlist plus a substring blocklist does not make argv
34
+ * safe, because the danger usually lives in a flag rather than in the program.
35
+ *
36
+ * rg --pre=/bin/sh runs an arbitrary program per file
37
+ * node --require=./evil.js loads a module before the script
38
+ * npm run <anything> runs whatever package.json says
39
+ * git --exec-path=/tmp relocates the helper binaries git then runs
40
+ *
41
+ * Each of those passes a program allowlist and carries no shell punctuation.
42
+ *
43
+ * So the shapes are enumerated instead. Only two are allowed, and both are
44
+ * things this project actually records: one of its own scripts, and grep. Every
45
+ * token beginning with a hyphen must appear in that program's flag allowlist,
46
+ * and no positional argument may look like a flag. Nothing here needs npm, git,
47
+ * sed or ripgrep, and breadth that is never used is only attack surface.
48
+ *
49
+ * This is a narrow gate, not a sandbox. A check that needs more than this
50
+ * should be run by a person who has read it.
51
+ */
52
+
53
+ /** Scripts of this project that may be run, and nothing else. */
54
+ const SCRIPT = /^(src\/cli\.mjs|scripts\/[A-Za-z0-9_\/-]+\.mjs)$/;
55
+
56
+ /**
57
+ * Flags each program may carry.
58
+ *
59
+ * grep cannot execute anything, but its arguments are still constrained so a
60
+ * pattern can never be read as a flag.
61
+ */
62
+ const FLAGS = {
63
+ grep: new Set(["-n", "-i", "-c", "-l", "-L", "-E", "-F", "-w", "-x", "-o", "-r", "-R", "-q", "-s", "-H", "-h", "-a"]),
64
+ // The script's own flags are read by the script, not by node, so they are
65
+ // constrained only by refusing anything that would change state.
66
+ node: null,
67
+ };
68
+
69
+ const CHANGES_STATE = /^--(apply|force|write|fix|delete|remove)\b/;
70
+
71
+ export const E = { UNSAFE: "E_UNSAFE_CHECK" };
72
+
73
+ /** Why a command will not be run here, or null when it will. */
74
+ export function refuseReason(command) {
75
+ const text = String(command ?? "").trim();
76
+ if (!text) return "no command recorded";
77
+ // Shell punctuation cannot do anything through execFile, but its presence
78
+ // means the command was written expecting a shell and will not behave.
79
+ if (/[;&|<>`$()]/.test(text)) return "written for a shell, so it cannot run without one";
80
+
81
+ const tokens = tokenize(text);
82
+ const [bin, ...rest] = tokens;
83
+
84
+ // A check that runs the checker is a loop, and it always reports a failure
85
+ // that says nothing: verify exits non-zero whenever anything could not run,
86
+ // so verify running verify can never pass however healthy the project is.
87
+ // The same shape made the integrity test plan unable to pass once it had
88
+ // failed once. A claim about verify has to be proven by watching it, not by
89
+ // asking it about itself.
90
+ if (/\bcli\.mjs\s+verify\b/.test(text)) {
91
+ return "it runs verify, which is what is running it, so the answer would only ever describe the loop";
92
+ }
93
+
94
+ if (bin === "node") {
95
+ // The script path must come first. A node option before it can load
96
+ // arbitrary code, which is the whole risk with running node at all.
97
+ const [script, ...args] = rest;
98
+ if (!script) return "node with no script";
99
+ if (!SCRIPT.test(script)) return `node may only run this project's own scripts, not ${script}`;
100
+ for (const a of args) {
101
+ if (CHANGES_STATE.test(a)) return "it would change something, and a check must not";
102
+ }
103
+ return null;
104
+ }
105
+
106
+ if (bin === "grep") {
107
+ const allowed = FLAGS.grep;
108
+ let seenSeparator = false;
109
+ let positionals = 0;
110
+ for (const a of rest) {
111
+ if (a === "--") { seenSeparator = true; continue; }
112
+ if (!seenSeparator && a.startsWith("-") && a !== "-") {
113
+ // Combined short flags are one token and several flags. Checking the
114
+ // token whole refused `grep -rn pattern src`, which is safe and
115
+ // ordinary, so each letter is checked instead. A long flag has no
116
+ // letters to split and is still checked as itself.
117
+ const combined = /^-[A-Za-z]{2,}$/.test(a)
118
+ ? [...a.slice(1)].map((letter) => `-${letter}`)
119
+ : [a];
120
+ for (const flag of combined) {
121
+ if (!allowed.has(flag)) {
122
+ return combined.length > 1
123
+ ? `grep flag ${flag}, part of ${a}, is not one this will run`
124
+ : `grep flag ${flag} is not one this will run`;
125
+ }
126
+ }
127
+ continue;
128
+ }
129
+ positionals += 1;
130
+ }
131
+ if (positionals < 1) return "grep with no pattern";
132
+ return null;
133
+ }
134
+
135
+ return `${bin} is not a program this will run unattended`;
136
+ }
137
+
138
+ /** Split on whitespace, keeping quoted runs whole. */
139
+ export function tokenize(command) {
140
+ const out = [];
141
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
142
+ let m;
143
+ while ((m = re.exec(command))) out.push(m[1] ?? m[2] ?? m[3]);
144
+ return out;
145
+ }
146
+
147
+ /**
148
+ * Run one recorded check.
149
+ *
150
+ * A non-zero exit that still produced output counts as a run that failed, not
151
+ * as a broken command: that is exactly the signal worth having, because a
152
+ * validator which starts finding things is how a claim stops being true.
153
+ */
154
+ export async function runCheck(root, command, { timeoutMs = 120000 } = {}) {
155
+ const refused = refuseReason(command);
156
+ if (refused) return { result: "error", detail: refused };
157
+
158
+ const [bin, ...rest] = tokenize(command);
159
+ try {
160
+ const { stdout } = await run(bin, rest, { cwd: root, timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024 });
161
+ return { result: "pass", detail: String(stdout).trim().split("\n").slice(-1)[0]?.slice(0, 160) ?? "" };
162
+ } catch (error) {
163
+ if (typeof error.code === "number") {
164
+ const out = `${error.stdout ?? ""}${error.stderr ?? ""}`.trim();
165
+ return { result: "fail", detail: out.split("\n").slice(-1)[0]?.slice(0, 160) || `exit ${error.code}` };
166
+ }
167
+ return { result: "error", detail: String(error.message).slice(0, 160) };
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Re-run every current piece of evidence that carries a command.
173
+ *
174
+ * Without `apply` nothing is written, which makes this safe to run at any time
175
+ * and is the mode worth putting in front of a person: it answers "is what we
176
+ * believe still true?" without changing the answer.
177
+ */
178
+ export async function verifyEvidence(root, { apply = false, taskId = null, limit = null } = {}) {
179
+ const rows = await query(root, (db) => db.all(
180
+ `SELECT e.id, e.task_id, e.summary, e.check_command, e.result, e.last_check_result, t.status AS task_status
181
+ FROM verification_evidence e
182
+ LEFT JOIN tasks t ON t.id = e.task_id
183
+ WHERE e.status = 'current'
184
+ ${taskId ? "AND e.task_id = ?" : ""}
185
+ ORDER BY e.id`,
186
+ ...(taskId ? [taskId] : []),
187
+ ));
188
+
189
+ const withCommand = rows.filter((r) => String(r.check_command ?? "").trim());
190
+ const manual = rows.length - withCommand.length;
191
+ const targets = limit ? withCommand.slice(0, limit) : withCommand;
192
+
193
+ const checked = [];
194
+ for (const row of targets) {
195
+ const outcome = await runCheck(root, row.check_command);
196
+ checked.push({ ...row, ...outcome });
197
+ }
198
+
199
+ const nowFailing = checked.filter((c) => c.result === "fail");
200
+ const errored = checked.filter((c) => c.result === "error");
201
+
202
+ if (apply && checked.length) {
203
+ const at = new Date().toISOString();
204
+ await mutate(root, async (db) => {
205
+ for (const c of checked) {
206
+ await db.run(
207
+ "UPDATE verification_evidence SET last_checked_at = ?, last_check_result = ? WHERE id = ?",
208
+ at, c.result, c.id,
209
+ );
210
+ // Only a check that ran and failed retracts the claim. A command that
211
+ // could not run says nothing about whether the product is correct, and
212
+ // marking evidence stale for it would turn a broken check into a false
213
+ // accusation against working code.
214
+ if (c.result === "fail") {
215
+ await db.run(
216
+ "UPDATE verification_evidence SET status = 'stale', stale_at = ? WHERE id = ?",
217
+ at, c.id,
218
+ );
219
+ }
220
+ }
221
+ });
222
+ }
223
+
224
+ return {
225
+ applied: apply,
226
+ evidenceCurrent: rows.length,
227
+ withCommand: withCommand.length,
228
+ manualOnly: manual,
229
+ checked: checked.length,
230
+ stillPassing: checked.filter((c) => c.result === "pass").length,
231
+ noLongerPassing: nowFailing.length,
232
+ couldNotRun: errored.length,
233
+ failing: nowFailing.map((c) => ({ evidence: c.id, task: c.task_id, taskStatus: c.task_status, command: c.check_command, detail: c.detail })),
234
+ unrunnable: errored.map((c) => ({ evidence: c.id, task: c.task_id, command: c.check_command, why: c.detail })),
235
+ };
236
+ }