@nanonets/graft 0.23.1 → 0.24.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 (266) hide show
  1. package/README.md +4 -4
  2. package/dist/ai/crux.d.ts +31 -3
  3. package/dist/ai/crux.d.ts.map +1 -1
  4. package/dist/ai/crux.js +88 -10
  5. package/dist/ai/crux.js.map +1 -1
  6. package/dist/ai/llm/openai.d.ts +101 -0
  7. package/dist/ai/llm/openai.d.ts.map +1 -1
  8. package/dist/ai/llm/openai.js +396 -6
  9. package/dist/ai/llm/openai.js.map +1 -1
  10. package/dist/ai/llm/recover-tool.d.ts +2 -0
  11. package/dist/ai/llm/recover-tool.d.ts.map +1 -1
  12. package/dist/ai/llm/recover-tool.js +1 -1
  13. package/dist/ai/llm/recover-tool.js.map +1 -1
  14. package/dist/ai/synthesize.d.ts.map +1 -1
  15. package/dist/ai/synthesize.js +100 -1
  16. package/dist/ai/synthesize.js.map +1 -1
  17. package/dist/claude/format.js +1 -1
  18. package/dist/claude/format.js.map +1 -1
  19. package/dist/claude/hooks.d.ts +0 -18
  20. package/dist/claude/hooks.d.ts.map +1 -1
  21. package/dist/claude/hooks.js +24 -77
  22. package/dist/claude/hooks.js.map +1 -1
  23. package/dist/claude/init.js +3 -3
  24. package/dist/claude/init.js.map +1 -1
  25. package/dist/claude/note-signals.d.ts +28 -0
  26. package/dist/claude/note-signals.d.ts.map +1 -0
  27. package/dist/claude/note-signals.js +325 -0
  28. package/dist/claude/note-signals.js.map +1 -0
  29. package/dist/claude/shim-template.d.ts.map +1 -1
  30. package/dist/claude/shim-template.js +10 -4
  31. package/dist/claude/shim-template.js.map +1 -1
  32. package/dist/claude/skill-template.d.ts.map +1 -1
  33. package/dist/claude/skill-template.js +49 -18
  34. package/dist/claude/skill-template.js.map +1 -1
  35. package/dist/claude/state.d.ts +4 -3
  36. package/dist/claude/state.d.ts.map +1 -1
  37. package/dist/claude/state.js.map +1 -1
  38. package/dist/claude/sync-run.js +1 -1
  39. package/dist/claude/sync-run.js.map +1 -1
  40. package/dist/cli-picker.d.ts +12 -5
  41. package/dist/cli-picker.d.ts.map +1 -1
  42. package/dist/cli-picker.js +24 -8
  43. package/dist/cli-picker.js.map +1 -1
  44. package/dist/cli.d.ts +2 -0
  45. package/dist/cli.d.ts.map +1 -1
  46. package/dist/cli.js +131 -38
  47. package/dist/cli.js.map +1 -1
  48. package/dist/context/batches.d.ts +29 -0
  49. package/dist/context/batches.d.ts.map +1 -0
  50. package/dist/context/batches.js +130 -0
  51. package/dist/context/batches.js.map +1 -0
  52. package/dist/context/build.d.ts +7 -2
  53. package/dist/context/build.d.ts.map +1 -1
  54. package/dist/context/build.js +84 -50
  55. package/dist/context/build.js.map +1 -1
  56. package/dist/context/node-file.d.ts.map +1 -1
  57. package/dist/context/node-file.js +41 -4
  58. package/dist/context/node-file.js.map +1 -1
  59. package/dist/engine.d.ts +2 -0
  60. package/dist/engine.d.ts.map +1 -1
  61. package/dist/engine.js +1 -0
  62. package/dist/engine.js.map +1 -1
  63. package/dist/graph/cards.d.ts.map +1 -1
  64. package/dist/graph/cards.js +9 -4
  65. package/dist/graph/cards.js.map +1 -1
  66. package/dist/graph/enrich.d.ts.map +1 -1
  67. package/dist/graph/enrich.js +8 -3
  68. package/dist/graph/enrich.js.map +1 -1
  69. package/dist/graph/generic.d.ts.map +1 -1
  70. package/dist/graph/generic.js +207 -6
  71. package/dist/graph/generic.js.map +1 -1
  72. package/dist/graph/lsp/client.d.ts +18 -0
  73. package/dist/graph/lsp/client.d.ts.map +1 -1
  74. package/dist/graph/lsp/client.js +40 -2
  75. package/dist/graph/lsp/client.js.map +1 -1
  76. package/dist/graph/lsp/enrich.d.ts.map +1 -1
  77. package/dist/graph/lsp/enrich.js +9 -1
  78. package/dist/graph/lsp/enrich.js.map +1 -1
  79. package/dist/graph/lsp/registry.d.ts +7 -0
  80. package/dist/graph/lsp/registry.d.ts.map +1 -1
  81. package/dist/graph/lsp/registry.js +27 -2
  82. package/dist/graph/lsp/registry.js.map +1 -1
  83. package/dist/graph/queries/gleam.scm +51 -0
  84. package/dist/graph/resolve.d.ts.map +1 -1
  85. package/dist/graph/resolve.js +65 -3
  86. package/dist/graph/resolve.js.map +1 -1
  87. package/dist/graph/workspace-cli.d.ts +2 -0
  88. package/dist/graph/workspace-cli.d.ts.map +1 -1
  89. package/dist/graph/workspace-cli.js +1 -1
  90. package/dist/graph/workspace-cli.js.map +1 -1
  91. package/dist/notes/branches.d.ts +7 -1
  92. package/dist/notes/branches.d.ts.map +1 -1
  93. package/dist/notes/branches.js +31 -3
  94. package/dist/notes/branches.js.map +1 -1
  95. package/dist/notes/cold-start.d.ts +12 -2
  96. package/dist/notes/cold-start.d.ts.map +1 -1
  97. package/dist/notes/cold-start.js +46 -5
  98. package/dist/notes/cold-start.js.map +1 -1
  99. package/dist/notes/credit.d.ts +37 -0
  100. package/dist/notes/credit.d.ts.map +1 -0
  101. package/dist/notes/credit.js +78 -0
  102. package/dist/notes/credit.js.map +1 -0
  103. package/dist/notes/find-notes.d.ts +17 -0
  104. package/dist/notes/find-notes.d.ts.map +1 -0
  105. package/dist/notes/find-notes.js +48 -0
  106. package/dist/notes/find-notes.js.map +1 -0
  107. package/dist/notes/flatten.d.ts +31 -0
  108. package/dist/notes/flatten.d.ts.map +1 -0
  109. package/dist/notes/flatten.js +73 -0
  110. package/dist/notes/flatten.js.map +1 -0
  111. package/dist/notes/git-facts.d.ts +2 -0
  112. package/dist/notes/git-facts.d.ts.map +1 -1
  113. package/dist/notes/git-facts.js +6 -0
  114. package/dist/notes/git-facts.js.map +1 -1
  115. package/dist/notes/home.d.ts +24 -6
  116. package/dist/notes/home.d.ts.map +1 -1
  117. package/dist/notes/home.js +82 -15
  118. package/dist/notes/home.js.map +1 -1
  119. package/dist/notes/learning-fields.d.ts +102 -0
  120. package/dist/notes/learning-fields.d.ts.map +1 -0
  121. package/dist/notes/learning-fields.js +214 -0
  122. package/dist/notes/learning-fields.js.map +1 -0
  123. package/dist/notes/memory-notes.d.ts +71 -0
  124. package/dist/notes/memory-notes.d.ts.map +1 -0
  125. package/dist/notes/memory-notes.js +239 -0
  126. package/dist/notes/memory-notes.js.map +1 -0
  127. package/dist/notes/note-gate.d.ts +42 -0
  128. package/dist/notes/note-gate.d.ts.map +1 -0
  129. package/dist/notes/note-gate.js +181 -0
  130. package/dist/notes/note-gate.js.map +1 -0
  131. package/dist/notes/notes.d.ts +29 -85
  132. package/dist/notes/notes.d.ts.map +1 -1
  133. package/dist/notes/notes.js +69 -254
  134. package/dist/notes/notes.js.map +1 -1
  135. package/dist/notes/pr-reviews.d.ts +35 -7
  136. package/dist/notes/pr-reviews.d.ts.map +1 -1
  137. package/dist/notes/pr-reviews.js +156 -46
  138. package/dist/notes/pr-reviews.js.map +1 -1
  139. package/dist/notes/secrets.d.ts +28 -0
  140. package/dist/notes/secrets.d.ts.map +1 -0
  141. package/dist/notes/secrets.js +362 -0
  142. package/dist/notes/secrets.js.map +1 -0
  143. package/dist/notes/targets.d.ts +37 -0
  144. package/dist/notes/targets.d.ts.map +1 -0
  145. package/dist/notes/targets.js +108 -0
  146. package/dist/notes/targets.js.map +1 -0
  147. package/dist/notes/team-check.d.ts +22 -0
  148. package/dist/notes/team-check.d.ts.map +1 -0
  149. package/dist/notes/team-check.js +130 -0
  150. package/dist/notes/team-check.js.map +1 -0
  151. package/dist/notes/trail-home.d.ts +3 -0
  152. package/dist/notes/trail-home.d.ts.map +1 -0
  153. package/dist/notes/trail-home.js +12 -0
  154. package/dist/notes/trail-home.js.map +1 -0
  155. package/dist/skills/answers-prompt.d.ts +13 -0
  156. package/dist/skills/answers-prompt.d.ts.map +1 -0
  157. package/dist/skills/answers-prompt.js +83 -0
  158. package/dist/skills/answers-prompt.js.map +1 -0
  159. package/dist/skills/never-save.d.ts +5 -0
  160. package/dist/skills/never-save.d.ts.map +1 -0
  161. package/dist/skills/never-save.js +158 -0
  162. package/dist/skills/never-save.js.map +1 -0
  163. package/dist/skills/params.d.ts +39 -0
  164. package/dist/skills/params.d.ts.map +1 -0
  165. package/dist/skills/params.js +275 -0
  166. package/dist/skills/params.js.map +1 -0
  167. package/dist/skills/ran-less.d.ts +5 -0
  168. package/dist/skills/ran-less.d.ts.map +1 -0
  169. package/dist/skills/ran-less.js +51 -0
  170. package/dist/skills/ran-less.js.map +1 -0
  171. package/dist/skills/risk-delta.d.ts +32 -0
  172. package/dist/skills/risk-delta.d.ts.map +1 -0
  173. package/dist/skills/risk-delta.js +56 -0
  174. package/dist/skills/risk-delta.js.map +1 -0
  175. package/dist/skills/risk.d.ts +73 -0
  176. package/dist/skills/risk.d.ts.map +1 -0
  177. package/dist/skills/risk.js +396 -0
  178. package/dist/skills/risk.js.map +1 -0
  179. package/dist/skills/types.d.ts +422 -0
  180. package/dist/skills/types.d.ts.map +1 -0
  181. package/dist/skills/types.js +2 -0
  182. package/dist/skills/types.js.map +1 -0
  183. package/dist/telemetry/contract.d.ts.map +1 -1
  184. package/dist/telemetry/contract.js +8 -6
  185. package/dist/telemetry/contract.js.map +1 -1
  186. package/dist/telemetry/notice.d.ts.map +1 -1
  187. package/dist/telemetry/notice.js +3 -1
  188. package/dist/telemetry/notice.js.map +1 -1
  189. package/dist/trace/check-reads.d.ts +36 -0
  190. package/dist/trace/check-reads.d.ts.map +1 -0
  191. package/dist/trace/check-reads.js +57 -0
  192. package/dist/trace/check-reads.js.map +1 -0
  193. package/dist/trace/host.d.ts +40 -0
  194. package/dist/trace/host.d.ts.map +1 -0
  195. package/dist/trace/host.js +84 -0
  196. package/dist/trace/host.js.map +1 -0
  197. package/dist/trace/redact.d.ts +8 -0
  198. package/dist/trace/redact.d.ts.map +1 -0
  199. package/dist/trace/redact.js +63 -0
  200. package/dist/trace/redact.js.map +1 -0
  201. package/dist/trace/seen-cache.d.ts +9 -0
  202. package/dist/trace/seen-cache.d.ts.map +1 -0
  203. package/dist/trace/seen-cache.js +46 -0
  204. package/dist/trace/seen-cache.js.map +1 -0
  205. package/dist/trace/shell-scan.d.ts +52 -0
  206. package/dist/trace/shell-scan.d.ts.map +1 -0
  207. package/dist/trace/shell-scan.js +318 -0
  208. package/dist/trace/shell-scan.js.map +1 -0
  209. package/dist/trace/shell-words.d.ts +91 -0
  210. package/dist/trace/shell-words.d.ts.map +1 -0
  211. package/dist/trace/shell-words.js +558 -0
  212. package/dist/trace/shell-words.js.map +1 -0
  213. package/dist/trace/shell.d.ts +91 -0
  214. package/dist/trace/shell.d.ts.map +1 -0
  215. package/dist/trace/shell.js +435 -0
  216. package/dist/trace/shell.js.map +1 -0
  217. package/dist/trace/state.d.ts +27 -0
  218. package/dist/trace/state.d.ts.map +1 -0
  219. package/dist/trace/state.js +98 -0
  220. package/dist/trace/state.js.map +1 -0
  221. package/dist/trace/tamper-code.d.ts +9 -0
  222. package/dist/trace/tamper-code.d.ts.map +1 -0
  223. package/dist/trace/tamper-code.js +174 -0
  224. package/dist/trace/tamper-code.js.map +1 -0
  225. package/dist/trace/tamper-git.d.ts +12 -0
  226. package/dist/trace/tamper-git.d.ts.map +1 -0
  227. package/dist/trace/tamper-git.js +77 -0
  228. package/dist/trace/tamper-git.js.map +1 -0
  229. package/dist/trace/types.d.ts +477 -0
  230. package/dist/trace/types.d.ts.map +1 -0
  231. package/dist/trace/types.js +12 -0
  232. package/dist/trace/types.js.map +1 -0
  233. package/dist/trace/walls-glob.d.ts +11 -0
  234. package/dist/trace/walls-glob.d.ts.map +1 -0
  235. package/dist/trace/walls-glob.js +264 -0
  236. package/dist/trace/walls-glob.js.map +1 -0
  237. package/dist/trace/walls-quoted.d.ts +25 -0
  238. package/dist/trace/walls-quoted.d.ts.map +1 -0
  239. package/dist/trace/walls-quoted.js +81 -0
  240. package/dist/trace/walls-quoted.js.map +1 -0
  241. package/dist/upkeep.d.ts +1 -1
  242. package/dist/upkeep.d.ts.map +1 -1
  243. package/dist/upkeep.js +4 -1
  244. package/dist/upkeep.js.map +1 -1
  245. package/dist/util/actions-folder.d.ts +2 -0
  246. package/dist/util/actions-folder.d.ts.map +1 -0
  247. package/dist/util/actions-folder.js +9 -0
  248. package/dist/util/actions-folder.js.map +1 -0
  249. package/dist/util/front-matter.d.ts +31 -4
  250. package/dist/util/front-matter.d.ts.map +1 -1
  251. package/dist/util/front-matter.js +95 -3
  252. package/dist/util/front-matter.js.map +1 -1
  253. package/dist/util/private-file.d.ts +14 -0
  254. package/dist/util/private-file.d.ts.map +1 -0
  255. package/dist/util/private-file.js +82 -0
  256. package/dist/util/private-file.js.map +1 -0
  257. package/dist/util/private-fs.d.ts +25 -0
  258. package/dist/util/private-fs.d.ts.map +1 -0
  259. package/dist/util/private-fs.js +253 -0
  260. package/dist/util/private-fs.js.map +1 -0
  261. package/dist/util/state.d.ts.map +1 -1
  262. package/dist/util/state.js +3 -0
  263. package/dist/util/state.js.map +1 -1
  264. package/dist/viewer/app.js +2 -2
  265. package/package.json +5 -5
  266. package/scripts/run-tests.mjs +14 -2
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Whether something `trail init` found is fit to offer as a team learning:
3
+ * it is tied to code that exists now, and the repo doesn't already say it.
4
+ *
5
+ * A learning a teammate's Claude acts on names the file or function it is
6
+ * about; that is also what lets trail hand it to Claude when Claude opens
7
+ * that file. One that names no code is a habit or a status line, and stays
8
+ * the person's own. One that CLAUDE.md, AGENTS.md or an existing learning
9
+ * already says adds nothing but noise.
10
+ */
11
+ import { existsSync, readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { contextDirFor } from "../context/node-file.js";
14
+ import { loadGraphCached } from "../graph/load.js";
15
+ import { listNotes, mentionedFiles } from "./notes.js";
16
+ const TOUCH_LIMIT = 10;
17
+ /** `foo_test.go`, `foo.test.ts`, `test/…`, `__tests__/…`, `spec/…`. */
18
+ const TEST_PATH = /(?:^|\/)(?:tests?|__tests__|spec)\/|[._-](?:test|spec)\.[A-Za-z]+$|_test\.go$/;
19
+ /** `Foo`, `Foo.bar`, `foo_bar()` in backticks: names a note can give a symbol by. */
20
+ const TICKED = /`([A-Za-z_$][\w$]*(?:(?:\.|::|#)[A-Za-z_$][\w$]*)*)(?:\(\))?`/g;
21
+ /**
22
+ * The code `text` is about, as `touches:` entries: files it names that exist
23
+ * in the checkout, `paths` that exist (an inline review comment's file), and
24
+ * `file#Symbol` for a backticked name the graph knows in one to three places.
25
+ * Empty when it names no code that exists now.
26
+ */
27
+ export function codeAnchors(repo, text, paths = []) {
28
+ const out = [];
29
+ const add = (t) => {
30
+ if (out.length < TOUCH_LIMIT && !out.includes(t))
31
+ out.push(t);
32
+ };
33
+ for (const f of mentionedFiles(repo, text))
34
+ add(f);
35
+ for (const p of paths)
36
+ if (p && !p.startsWith("..") && existsSync(join(repo, p)))
37
+ add(p);
38
+ const graph = loadGraphCached(contextDirFor(repo));
39
+ if (!graph)
40
+ return out;
41
+ const names = new Set();
42
+ for (const m of text.matchAll(TICKED)) {
43
+ const last = m[1].split(/\.|::|#/).pop();
44
+ // Short or generic words (`id`, `run`) match too much to mean anything.
45
+ if (last.length >= 4)
46
+ names.add(last);
47
+ }
48
+ if (!names.size)
49
+ return out;
50
+ const byName = new Map();
51
+ for (const n of graph.nodes) {
52
+ if (n.kind === "file" || !names.has(n.name))
53
+ continue;
54
+ const list = byName.get(n.name) ?? [];
55
+ list.push(n.id);
56
+ byName.set(n.name, list);
57
+ }
58
+ for (const ids of byName.values()) {
59
+ // A test's fake of a function isn't what the note is about, when the real one is there.
60
+ const real = ids.filter((id) => !TEST_PATH.test(id.split("#")[0]));
61
+ const pick = real.length ? real : ids;
62
+ if (pick.length <= 3)
63
+ for (const id of pick)
64
+ add(id);
65
+ }
66
+ return out;
67
+ }
68
+ const STOP = new Set("the and for with from that this what when where which into does have has had been being they them their there then than also only just more most some such very will would should could must about after before over under using used make makes made keep keeps kept your yours ours here every each other".split(" "));
69
+ /** The words that carry meaning: four letters or more, not filler. */
70
+ export function contentWords(s) {
71
+ return new Set(s
72
+ .toLowerCase()
73
+ .split(/[^a-z0-9_]+/)
74
+ .filter((w) => w.length >= 4 && !STOP.has(w)));
75
+ }
76
+ /** Paragraphs and list items of a markdown text, each as its content words. */
77
+ function chunks(text) {
78
+ return text
79
+ .split(/\n\s*\n|\n(?=\s*(?:[-*+]|\d+\.)\s)/)
80
+ .map(contentWords)
81
+ .filter((w) => w.size >= 3);
82
+ }
83
+ function readText(path) {
84
+ try {
85
+ return readFileSync(path, "utf8");
86
+ }
87
+ catch {
88
+ return "";
89
+ }
90
+ }
91
+ export function knownText(repo) {
92
+ const docs = ["CLAUDE.md", "AGENTS.md", ".claude/CLAUDE.md"].map((f) => readText(join(repo, f)));
93
+ let notes = [];
94
+ try {
95
+ notes = listNotes(repo, { branches: false });
96
+ }
97
+ catch {
98
+ /* no learnings yet */
99
+ }
100
+ return {
101
+ chunks: [...docs.flatMap(chunks), ...notes.flatMap((n) => chunks(n.body))],
102
+ titles: notes.map((n) => contentWords(n.title)).filter((w) => w.size > 0),
103
+ };
104
+ }
105
+ /**
106
+ * Whether the repo already says what a candidate says: most of its title's
107
+ * and lead's words in one paragraph of CLAUDE.md, AGENTS.md or a learning, or
108
+ * a learning with nearly the same title.
109
+ */
110
+ export function alreadySaid(known, title, body) {
111
+ const t = contentWords(title);
112
+ if (t.size && known.titles.some((k) => overlap(t, k) >= 0.8 && overlap(k, t) >= 0.8))
113
+ return true;
114
+ const lead = body.replace(/^#+ .*$/gm, "").trim().slice(0, 400);
115
+ const words = contentWords(`${title} ${lead}`);
116
+ if (words.size < 5)
117
+ return false;
118
+ return known.chunks.some((c) => overlap(words, c) >= 0.6);
119
+ }
120
+ /** The share of `a`'s words that are in `b`. */
121
+ function overlap(a, b) {
122
+ if (!a.size)
123
+ return 0;
124
+ let n = 0;
125
+ for (const w of a)
126
+ if (b.has(w))
127
+ n++;
128
+ return n / a.size;
129
+ }
130
+ //# sourceMappingURL=team-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-check.js","sourceRoot":"","sources":["../../src/notes/team-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** `~/.trail`, or `$TRAIL_HOME`. */
2
+ export declare function trailHome(env?: NodeJS.ProcessEnv, home?: string): string;
3
+ //# sourceMappingURL=trail-home.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trail-home.d.ts","sourceRoot":"","sources":["../../src/notes/trail-home.ts"],"names":[],"mappings":"AAQA,oCAAoC;AACpC,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEhG"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `~/.trail` on its own, so a hook's fast path (the trace layer, `trail keep`'s
3
+ * check before a shell command) can find it without loading notes/home.ts and
4
+ * the front-matter parser that file needs to count learnings.
5
+ */
6
+ import { homedir } from "node:os";
7
+ import { join, resolve } from "node:path";
8
+ /** `~/.trail`, or `$TRAIL_HOME`. */
9
+ export function trailHome(env = process.env, home = homedir()) {
10
+ return env.TRAIL_HOME ? resolve(env.TRAIL_HOME) : join(home, ".trail");
11
+ }
12
+ //# sourceMappingURL=trail-home.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trail-home.js","sourceRoot":"","sources":["../../src/notes/trail-home.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,oCAAoC;AACpC,MAAM,UAAU,SAAS,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,OAAe,OAAO,EAAE;IACtF,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** The command, a whole Bash call, answers a login or confirmation prompt for the person. */
2
+ export declare function answersPrompt(cmd: string): boolean;
3
+ /**
4
+ * D116 item 5: the whole call only feeds keystrokes into a login (`(sleep 15; printf '\n'; sleep 300) | script -q
5
+ * /dev/null npm login`, `yes | npx vercel login`, `npm login <<< ...`): the login and its service, else null. Such a
6
+ * step is dropped from a skill and the login becomes a "Before you start" line for the person; what was fed is never
7
+ * kept. A feed into a publish, `expect`, or a login inside a longer call is not one (still `answersPrompt`).
8
+ */
9
+ export declare function loginFeed(cmd: string): {
10
+ service: string;
11
+ login: string;
12
+ } | null;
13
+ //# sourceMappingURL=answers-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answers-prompt.d.ts","sourceRoot":"","sources":["../../src/skills/answers-prompt.ts"],"names":[],"mappings":"AAwBA,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAgCD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWhF"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * A step that answers a login or confirmation prompt for the person (D101
3
+ * item 4): keystrokes piped or fed into `script`, `expect` or a login or
4
+ * publish (`(sleep 15; printf '\n'; sleep 300) | script -q /dev/null npm
5
+ * login`, `yes | npm publish`, `printf 'y\n' | npm login`, `npm login <<<
6
+ * ...`), or `expect` run at all. Such a step is never kept: the prompt it
7
+ * answers is the person's, and an agent rightly reads a kept one as a way
8
+ * round their confirmation.
9
+ *
10
+ * Linear: one top-level split per level, groups `( … )` and `{ …; }` read
11
+ * at most 3 levels deep, and patterns with no nested repetition.
12
+ */
13
+ import { bareCommand, splitTop } from '../trace/shell.js';
14
+ import { gatedKind, loginOf } from './risk.js';
15
+ const MAX_DEPTH = 3;
16
+ /** A program that writes keystrokes: `printf`, `echo` or `yes` at a command's start. */
17
+ const FEEDER = /(?:^|[;&|({]\s*)(?:printf|echo|yes)(?![\w.-])/;
18
+ /** Programs that drive another program's terminal prompt. */
19
+ const PROMPT_DRIVER = /^(?:script|expect|unbuffer)(?![\w.-])/;
20
+ const EXPECT = /^expect(?![\w.-])/;
21
+ const HERE_STRING = /<<</;
22
+ const STDIN_FLAG = /\s--(?:password-stdin|with-token|stdin)(?![\w-])/;
23
+ /** The command, a whole Bash call, answers a login or confirmation prompt for the person. */
24
+ export function answersPrompt(cmd) {
25
+ return within(String(cmd ?? ''), 0);
26
+ }
27
+ function within(text, depth) {
28
+ const segs = splitTop(text);
29
+ for (let i = 0; i < segs.length; i++) {
30
+ const seg = segs[i];
31
+ const t = seg.text.trim();
32
+ if (EXPECT.test(bareCommand(t)))
33
+ return true;
34
+ if (depth < MAX_DEPTH && (t.startsWith('(') || t.startsWith('{'))) {
35
+ const inner = t.replace(/^[({]/, '').replace(/[)}]$/, '');
36
+ if (within(inner, depth + 1))
37
+ return true;
38
+ }
39
+ if (HERE_STRING.test(t) && promptTarget(t))
40
+ return true;
41
+ const next = segs[i + 1];
42
+ if (seg.sep === '|' && next && FEEDER.test(t) && promptTarget(next.text))
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ /** What a fed keystroke would answer: a prompt driver, or a login or publish (through its wrappers, risk.ts). */
48
+ function promptTarget(text) {
49
+ const t = text.trim();
50
+ if (PROMPT_DRIVER.test(bareCommand(t)))
51
+ return true;
52
+ // A flag made to read stdin (`docker login --password-stdin`, `gh auth login --with-token`) is input, not a prompt.
53
+ if (STDIN_FLAG.test(t))
54
+ return false;
55
+ const kind = gatedKind(t);
56
+ return kind === 'login' || kind === 'publish';
57
+ }
58
+ /** A group or a command that only waits and types: `(sleep 15; printf '\n'; sleep 300)`, `printf 'y\n'`, `yes`. */
59
+ const KEYSTROKES = /^(?:[({]\s*)?(?:(?:sleep\s+[\d.]+|printf\s+(?:'[^']*'|"[^"]*"|[^\s;&|()'"])+|echo(?:\s+(?:'[^']*'|"[^"]*"|[^\s;&|()'"])+)*|yes)\s*(?:;\s*|$|(?=[)}])))+(?:[)}]\s*)?$/;
60
+ /**
61
+ * D116 item 5: the whole call only feeds keystrokes into a login (`(sleep 15; printf '\n'; sleep 300) | script -q
62
+ * /dev/null npm login`, `yes | npx vercel login`, `npm login <<< ...`): the login and its service, else null. Such a
63
+ * step is dropped from a skill and the login becomes a "Before you start" line for the person; what was fed is never
64
+ * kept. A feed into a publish, `expect`, or a login inside a longer call is not one (still `answersPrompt`).
65
+ */
66
+ export function loginFeed(cmd) {
67
+ const text = String(cmd ?? '').trim();
68
+ if (!answersPrompt(text))
69
+ return null;
70
+ const segs = splitTop(text);
71
+ let target;
72
+ if (segs.length === 1 && HERE_STRING.test(text))
73
+ target = text.slice(0, text.search(HERE_STRING));
74
+ else if (segs.length === 2 && segs[0].sep === '|' && segs[0].text.length <= 4096 && KEYSTROKES.test(segs[0].text.trim()))
75
+ target = segs[1].text;
76
+ else
77
+ return null;
78
+ const bare = bareCommand(target.trim());
79
+ if (EXPECT.test(bare) || gatedKind(bare) !== 'login')
80
+ return null;
81
+ return loginOf(bare);
82
+ }
83
+ //# sourceMappingURL=answers-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answers-prompt.js","sourceRoot":"","sources":["../../src/skills/answers-prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Reason a command can never be in a skill (4.11), or null. */
2
+ export declare function neverSave(cmd: string): string | null;
3
+ /** 4.10 backstop: a password written out after a flag that takes one. The fold names most secrets; these it can't tell from a word. */
4
+ export declare function literalPassword(cmd: string): boolean;
5
+ //# sourceMappingURL=never-save.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"never-save.d.ts","sourceRoot":"","sources":["../../src/skills/never-save.ts"],"names":[],"mappings":"AA2HA,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWpD;AAaD,uIAAuI;AACvI,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGpD"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Never save (design 4.11, spec 10): a command a skill may never hold, so a
3
+ * run with one gives no skill at all (sudo, a download piped into a shell,
4
+ * `rm -rf` on a variable or a home path, a force push to a shared branch, a
5
+ * credential file, destructive SQL off this machine, anything that messages
6
+ * people, Trail itself), and the 4.10 password backstop. Split from risk.ts,
7
+ * whose gated list and agent guard the PreToolUse fast path loads; this file
8
+ * is for the save side only.
9
+ */
10
+ import { isTrailCommand } from '../trace/shell.js';
11
+ import { argsAfter, pushes, unknownDst, word, written } from './risk.js';
12
+ const LOCAL_HOSTS = /^(?:localhost|127\.0\.0\.1|::1|\[::1\])$/i;
13
+ const escapeRe = (t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
14
+ /** Hosts a database command names: `-h x`, `--host=x`, `host=x`, `scheme://user@x:port`. */
15
+ function namedHosts(cmd) {
16
+ const hosts = [];
17
+ for (const m of cmd.matchAll(/(?:^|\s)(?:-h|--host)(?:=|\s+)['"]?([^\s'"/;]+)/g))
18
+ hosts.push(m[1]);
19
+ for (const m of cmd.matchAll(/\bhost=['"]?([^\s'";]+)/gi))
20
+ hosts.push(m[1]);
21
+ for (const m of cmd.matchAll(/\b[a-z][a-z0-9+.-]*:\/\/(?:[^\s/@'"]*@)?(\[[^\]]+\]|[^\s/:'"?]+)/gi))
22
+ hosts.push(m[1]);
23
+ return hosts;
24
+ }
25
+ /** A target `rm -r` or `find -delete` must never get: a variable, a {param}, home, an absolute path, the whole folder. */
26
+ const wildTarget = (t) => t !== '{}' && (/^[$~/]|^\.\.(?:\/|$)/.test(t) || /\$|`|\{[A-Za-z_]\w*\}/.test(t) || /^(?:\.\/?|(?:\.\/)?\*)$/.test(t));
27
+ function rmWild(text) {
28
+ const rms = [...text.matchAll(word('rm'))];
29
+ for (const [i, m] of rms.entries()) {
30
+ let recursive = false;
31
+ let opts = true;
32
+ const targets = [];
33
+ for (const w of argsAfter(text, m.index + m[0].length, rms[i + 1]?.index)) {
34
+ if (opts && w === '--')
35
+ opts = false;
36
+ else if (opts && /^-[a-zA-Z]+$/.test(w))
37
+ recursive ||= /[rR]/.test(w);
38
+ else if (opts && /^--[\w-]+$/.test(w))
39
+ recursive ||= w === '--recursive';
40
+ else
41
+ targets.push(w);
42
+ }
43
+ if (!recursive)
44
+ continue;
45
+ // `xargs rm -rf`: the targets come from elsewhere.
46
+ if (!targets.length ? /\bxargs\b[^;&|\n]*$/.test(text.slice(0, m.index)) : targets.some(wildTarget))
47
+ return true;
48
+ }
49
+ const finds = [...text.matchAll(word('find'))];
50
+ for (const [i, m] of finds.entries()) {
51
+ const w = argsAfter(text, m.index + m[0].length, finds[i + 1]?.index);
52
+ const roots = w.slice(0, Math.max(0, w.findIndex((x) => /^[-(!]/.test(x))));
53
+ const deletes = w.includes('-delete') || w.some((x, i) => /^-(?:exec|execdir|ok|okdir)$/.test(x) && /(?:^|\/)rm$/.test(w[i + 1] ?? ''));
54
+ // `find . -name x -exec rm {} +` is fine; a root the skill can't know is not.
55
+ if (deletes && roots.some((t) => /^[$~/{]/.test(t) || /\$|`/.test(t)))
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+ const SHARED = new Set(['main', 'master', 'prod', 'production', 'beta', 'dev', 'develop', 'staging', 'trunk']);
61
+ const forcePushShared = (c) => pushes(c).some((p) => p.force && p.dsts.some((d) => SHARED.has(d.toLowerCase()) || unknownDst(d)));
62
+ const SUDO = /(?:^|[^\w./-])(?:[\w./-]*\/)?(?:sudo|doas|pkexec)(?![\w-])/i;
63
+ const SHELL_WORD = String.raw `(?:sudo\s+(?:-\S+\s+)*)?(?:env\s+(?:\S+=\S*\s+)*)?(?:[\w./-]*\/)?`;
64
+ const DOWNLOAD = [
65
+ new RegExp(String.raw `\b(?:curl|wget)\b[^\n]*?(?<!\|)\|(?!\|)\s*${SHELL_WORD}(?:ba|z|da|k|fi)?sh\b`, 'i'),
66
+ new RegExp(String.raw `\b(?:curl|wget)\b[^\n]*?(?<!\|)\|(?!\|)\s*${SHELL_WORD}(?:python[\d.]*|node|perl|ruby|php)(?:\s+-)?\s*(?=$|[;&|)\n])`, 'i'),
67
+ /(?:\b(?:ba|z|da|k)?sh|\bsource|(?:^|[\s;&|(])\.)\s+<\(\s*(?:curl|wget)\b/i,
68
+ /\b(?:(?:ba|z|da|k)?sh\s+-\w*c|eval)\s+["']?(?:\$\(|`)\s*(?:curl|wget)\b/i,
69
+ ];
70
+ /** `curl -o x.sh URL && sh x.sh`, `wget URL -O s.sh; chmod +x s.sh; ./s.sh`: a download saved to a file, then run. */
71
+ function downloadThenRun(text) {
72
+ for (const m of text.matchAll(/\b(curl|wget)\b([^;&|\n]*)/gi)) {
73
+ const [, tool, args] = m;
74
+ const files = new Set();
75
+ const named = /(?:\s-[a-zA-Z]*o\s*|\s--output[= ]\s*|\s-O\s*(?=[^\s-])|\s--output-document[= ]\s*)['"]?([^\s'";&|]+)/.exec(args)?.[1];
76
+ if (named && named !== '-' && named !== '/dev/null')
77
+ files.add(named);
78
+ // `wget URL` and `curl -O URL` save under the link's own name.
79
+ const remote = /^wget$/i.test(tool) ? !/\s-[a-zA-Z]*O\b|--output-document/.test(args) : /\s-[a-zA-Z]*O\b|--remote-name\b/.test(args);
80
+ if (remote)
81
+ for (const u of args.matchAll(/https?:\/\/\S*\/([^\s/'"?#]+)/g))
82
+ files.add(u[1]);
83
+ const after = text.slice(m.index + m[0].length);
84
+ for (const f of files) {
85
+ const e = escapeRe(f.replace(/^\.\//, '').split('/').pop());
86
+ // Run as a program, or handed to a shell, `source`, `.` or `chmod +x`: at the start of a command.
87
+ const run = new RegExp(String.raw `(?:^|[;&|(\n]|&&|\|\|)\s*(?:(?:(?:ba|z|da|k)?sh|source|\.|chmod\s+\S*x)\s+)?['"]?(?:\.?\.?\/[^\s'"]*\/|\.\/)?${e}['"]?(?=\s|$|[;&|)])`);
88
+ const plainName = new RegExp(String.raw `(?:^|[;&|(\n]|&&|\|\|)\s*(?:(?:ba|z|da|k)?sh|source|\.|chmod\s+\S*x)\s+['"]?${e}['"]?(?=\s|$|[;&|)])`);
89
+ if (plainName.test(after) || (run.test(after) && new RegExp(String.raw `(?:^|[\s;&|(])\.{0,2}\/(?:[^\s'"]*\/)?${e}(?=\s|$|[;&|)'"])`).test(after)))
90
+ return true;
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ const CREDENTIAL = /(?:\.npmrc|\.netrc|\.aws\/(?:credentials|sso\/cache|cli\/cache)|(?<![\w.])\.ssh\/|\.git-credentials|\.docker\/config\.json|gh\/hosts\.yml|com\.vercel\.cli\/auth\.json|\.config\/gcloud|Library\/Keychains|\.kube\/config|\.pypirc|\.gem\/credentials|\.cargo\/credentials|\.pgpass|\.my\.cnf|credentials\.tfrc\.json|\.terraformrc|\.config\/hub\b|\.azure\/|\.password-store|\.vault-token)/i;
96
+ /**
97
+ * Commands that store a credential they were handed where every later command picks it up. Logging in at
98
+ * the browser (`npm login`, `gh auth login`) needs the person there, holds no secret in the step, and is how
99
+ * a release gets past an expired token: not on this list, but gated (D93, `login`), so it is never run unasked.
100
+ */
101
+ const CREDENTIAL_WRITE = /\bnpm\s+config\s+set\s+\S*_(?:auth|password)|\b(?:docker|podman|helm\s+registry)\s+login\b[^;&|\n]*\s(?:-p|--password(?:-stdin)?)\b|\baws\s+configure\s+set\b|\bgit\s+credential\s+(?:approve|store)\b|\bgit\s+config\b[^;&|\n]*\bcredential\.helper\b|\bsecurity\s+add-(?:generic|internet)-password\b/i;
102
+ /** Commands that print a stored credential. */
103
+ const CREDENTIAL_READ = /\bgh\s+auth\s+token\b|\bgit\s+credential\s+fill\b|\bsecurity\s+(?:find|delete)-(?:generic|internet)-password\b|\bsecurity\s+dump-keychain\b|\bop\s+(?:read|item\s+get)\b|\bgcloud\s+auth\s+print-(?:access|identity)-token\b|\bheroku\s+auth:token\b|\bpass\s+show\b|\bvault\s+(?:read|kv\s+get)\b/i;
104
+ const DESTRUCTIVE_SQL = /\b(?:drop\s+(?:table|database|schema|view|materialized\s+view|index|owned|role|user|function|type|sequence|extension)|truncate(?!\s+-)|delete\s+from|alter\s+table\b[^;]*?\bdrop)\b|\bdropdb\b|\bflush(?:all|db)\b|\bdropDatabase\s*\(|\.drop(?:Collection)?\s*\(\s*\)|\bdeleteMany\s*\(\s*\{\s*\}\s*\)|\bsupabase\s+db\s+reset\b[^;&|\n]*--(?:linked|db-url)|\bprisma\s+(?:migrate\s+reset|db\s+push\b[^;&|\n]*--(?:force-reset|accept-data-loss))|\brails\s+db:(?:drop|reset)\b/i;
105
+ const MESSAGES = /\b(?:sendmail|mail\s+-s|mailx|mutt|msmtp|swaks|twilio)\b|\bgit\s+send-email\b|hooks\.slack\.com|\bslack\s+chat\b|graph\.facebook\.com|discord(?:app)?\.com\/api\/webhooks|api\.telegram\.org\/bot|api\.(?:sendgrid\.com|mailgun\.net|postmarkapp\.com|resend\.com)|\bgh\s+(?:issue\s+(?:create|comment)|pr\s+(?:comment|review))\b/i;
106
+ /** `gh api` that writes: a POST/PATCH/PUT, or fields (`-f`, `-F`, `--input`) without `-X GET`. */
107
+ function ghApiWrite(c) {
108
+ if (!/\bgh\s+api\b/i.test(c))
109
+ return false;
110
+ if (/\bgh\s+api\s+graphql\b/i.test(c))
111
+ return /\bmutation\b[\s\S]*?\b(?:add|create|submit|update)\w*(?:Comment|Review|Issue|Discussion)\w*\b/i.test(c);
112
+ const method = /(?:-X|--method)[\s=]+([A-Z]+)/i.exec(c)?.[1]?.toUpperCase();
113
+ const writes = method ? method !== 'GET' : /(?:^|\s)(?:-f|-F|--field|--raw-field|--input)(?=[\s=])/.test(c);
114
+ return writes && /\/(?:comments|reviews|issues|messages|replies)\b/i.test(c);
115
+ }
116
+ /** Trail run by a wrapper `isTrailCommand` doesn't open: `eval "trail …"`, `xargs trail`, `find -exec trail`. */
117
+ const TRAIL_VIA = /(?:\beval|\bxargs(?:\s+-\S+(?:\s+[^\s-]\S*)?)*|(?<!\S)-(?:exec|execdir|ok|okdir))\s+["']?(?:trail|graft|trail-code)\b/i;
118
+ const NEVER = [
119
+ ['runs sudo', (c) => SUDO.test(c)],
120
+ ['pipes a download into a shell', (c) => DOWNLOAD.some((re) => re.test(c)) || downloadThenRun(c)],
121
+ ['runs rm -rf on a variable or a home path', rmWild],
122
+ ['force-pushes a shared branch', forcePushShared],
123
+ ['touches a credential file', (c) => CREDENTIAL.test(c) || CREDENTIAL_WRITE.test(c) || CREDENTIAL_READ.test(c)],
124
+ ['runs destructive SQL off this machine', (c) => DESTRUCTIVE_SQL.test(c)
125
+ && (namedHosts(c).some((h) => !LOCAL_HOSTS.test(h)) || !/(?:^|[;&|(]\s*)docker\s+exec\b/i.test(c))],
126
+ ['messages people or submits to an outside service', (c) => MESSAGES.test(c) || ghApiWrite(c)],
127
+ ['runs Trail itself', (c) => isTrailCommand(c) || TRAIL_VIA.test(c)],
128
+ ];
129
+ /** Reason a command can never be in a skill (4.11), or null. */
130
+ export function neverSave(cmd) {
131
+ const c = String(cmd ?? '');
132
+ for (const [why, hit] of NEVER) {
133
+ if (!hit(c))
134
+ continue;
135
+ if (why === 'touches a credential file') {
136
+ const writes = CREDENTIAL_WRITE.test(c) || />>?\s*\S*(?:\.npmrc|\.netrc|credentials|\.ssh\/|config\.json|hosts\.yml|auth\.json|\.kube\/config|\.pypirc|\.pgpass)|\b(?:cp|mv|tee|ln)\b|security\s+add-/i.test(c);
137
+ return writes ? 'writes a credential file' : 'reads a credential file';
138
+ }
139
+ return why;
140
+ }
141
+ return null;
142
+ }
143
+ const PASSWORD_AT = [
144
+ /\b(?:mysql|mysqldump|mysqladmin|mariadb|mariadb-dump)\b[^;&|\n]*?\s-p(?=\S)(\S+)/i,
145
+ /\b(?:mysql|mysqldump|mysqladmin|mariadb)\b[^;&|\n]*?\s--password=(\S+)/i,
146
+ /\b(?:docker|podman|nerdctl|buildah|skopeo|oras)\s+login\b[^;&|\n]*?\s(?:-p|--password)(?:=|\s+)(\S+)/i,
147
+ /\bhelm\s+registry\s+login\b[^;&|\n]*?\s(?:-p|--password)(?:=|\s+)(\S+)/i,
148
+ /\bsshpass\s+-p\s*(\S+)/i,
149
+ /\bredis-cli\b[^;&|\n]*?\s(?:-a|--pass)\s+(\S+)/i,
150
+ /\b(?:echo|printf)\s+(?:-n\s+)?(\S+)\s*\|[^;&\n]*--password-stdin\b/i,
151
+ /\b(?:PGPASSWORD|MYSQL_PWD|REDISCLI_AUTH|SSHPASS)=(\S+)/,
152
+ ];
153
+ /** 4.10 backstop: a password written out after a flag that takes one. The fold names most secrets; these it can't tell from a word. */
154
+ export function literalPassword(cmd) {
155
+ const c = String(cmd ?? '');
156
+ return PASSWORD_AT.some((re) => { const v = re.exec(c)?.[1]; return v !== undefined && written(v); });
157
+ }
158
+ //# sourceMappingURL=never-save.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"never-save.js","sourceRoot":"","sources":["../../src/skills/never-save.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Parameters in a skill's steps (design 4.5 step 10).
3
+ *
4
+ * A step copied from a run carries last time's values: the version it
5
+ * published, the commit it pushed, the PR it merged, the port the server ran
6
+ * on. Replayed as they are, they would publish the old version again or push
7
+ * an old commit. So values of a known shape become `{name}`, every occurrence
8
+ * of one value under one name, in this order: version, sha, pr, port, branch,
9
+ * date. A sha defaults to `$(git rev-parse HEAD)` (when it is the only one) and a date to today; a port
10
+ * or a branch defaults to the value seen; a version and a PR come from the ask.
11
+ * With the last run of the same job beside this one, a token that differed
12
+ * between the two (the package a test run named) becomes a parameter too.
13
+ *
14
+ * Pure: no files, no clock.
15
+ */
16
+ import type { PromptParams } from '../trace/types.js';
17
+ import type { ParamDef } from './types.js';
18
+ export declare const STABLE_BRANCHES: ReadonlySet<string>;
19
+ /** Finds shape and varied tokens in kept commands, replaces them with {name}, returns defs (4.5 step 10). */
20
+ export declare function findParams(cmds: string[], ctx: {
21
+ prompt: PromptParams;
22
+ prior?: string[] | null;
23
+ check?: string;
24
+ }): {
25
+ cmds: string[];
26
+ check?: string;
27
+ params: ParamDef[];
28
+ };
29
+ /** Unknown {x} left as is. */
30
+ export declare function fillParams(text: string, values: Record<string, string>): string;
31
+ /** Values for a run block: prompt shapes first, then defaults, then last seen (with a note). */
32
+ export declare function resolveParams(defs: ParamDef[], prompt: PromptParams): {
33
+ values: Record<string, string>;
34
+ notes: string[];
35
+ };
36
+ /** 'version, sha=$(git rev-parse HEAD), pkg=workflow'. */
37
+ export declare function paramsToMeta(defs: ParamDef[]): string;
38
+ export declare function metaToParams(s: string | undefined): ParamDef[];
39
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../src/skills/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAAmG,CAAC;AAkMpJ,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAA;CAAE,CAYzK;AAED,8BAA8B;AAC9B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAE/E;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBzH;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAErD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,EAAE,CAY9D"}