@aligndottech/cli 0.57.1 → 0.59.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.
- package/README.md +31 -18
- package/dist/bin.js +3 -0
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/import/git.d.ts.map +1 -1
- package/dist/commands/import/git.js +8 -4
- package/dist/commands/import/git.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +5 -4
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +26 -12
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/use.d.ts +2 -0
- package/dist/commands/use.d.ts.map +1 -1
- package/dist/commands/use.js +19 -1
- package/dist/commands/use.js.map +1 -1
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/commands/why.js +111 -55
- package/dist/commands/why.js.map +1 -1
- package/dist/lib/agent-rules.d.ts +11 -4
- package/dist/lib/agent-rules.d.ts.map +1 -1
- package/dist/lib/agent-rules.js +163 -190
- package/dist/lib/agent-rules.js.map +1 -1
- package/dist/lib/ask-listing.d.ts +30 -0
- package/dist/lib/ask-listing.d.ts.map +1 -0
- package/dist/lib/ask-listing.js +60 -0
- package/dist/lib/ask-listing.js.map +1 -0
- package/dist/lib/capture-report.d.ts.map +1 -1
- package/dist/lib/capture-report.js +11 -3
- package/dist/lib/capture-report.js.map +1 -1
- package/dist/lib/claude-presence.d.ts +12 -0
- package/dist/lib/claude-presence.d.ts.map +1 -0
- package/dist/lib/claude-presence.js +19 -0
- package/dist/lib/claude-presence.js.map +1 -0
- package/dist/lib/doctor.d.ts +9 -1
- package/dist/lib/doctor.d.ts.map +1 -1
- package/dist/lib/doctor.js +19 -1
- package/dist/lib/doctor.js.map +1 -1
- package/dist/lib/embedding-assets.d.ts +40 -0
- package/dist/lib/embedding-assets.d.ts.map +1 -0
- package/dist/lib/embedding-assets.js +74 -0
- package/dist/lib/embedding-assets.js.map +1 -0
- package/dist/lib/embedding-runtime.d.ts +41 -0
- package/dist/lib/embedding-runtime.d.ts.map +1 -0
- package/dist/lib/embedding-runtime.js +224 -0
- package/dist/lib/embedding-runtime.js.map +1 -0
- package/dist/lib/fetchers/git.d.ts +3 -0
- package/dist/lib/fetchers/git.d.ts.map +1 -1
- package/dist/lib/fetchers/git.js +9 -5
- package/dist/lib/fetchers/git.js.map +1 -1
- package/dist/lib/git.d.ts +7 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +7 -2
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/import-defaults.d.ts +11 -4
- package/dist/lib/import-defaults.d.ts.map +1 -1
- package/dist/lib/import-defaults.js +11 -4
- package/dist/lib/import-defaults.js.map +1 -1
- package/dist/lib/launch/mcp-child-env.d.ts.map +1 -1
- package/dist/lib/launch/mcp-child-env.js +6 -4
- package/dist/lib/launch/mcp-child-env.js.map +1 -1
- package/dist/lib/local-db.d.ts +13 -0
- package/dist/lib/local-db.d.ts.map +1 -1
- package/dist/lib/local-db.js +23 -0
- package/dist/lib/local-db.js.map +1 -1
- package/dist/lib/local-embeddings-native.d.ts +17 -0
- package/dist/lib/local-embeddings-native.d.ts.map +1 -0
- package/dist/lib/local-embeddings-native.js +78 -0
- package/dist/lib/local-embeddings-native.js.map +1 -0
- package/dist/lib/local-embeddings.d.ts.map +1 -1
- package/dist/lib/local-embeddings.js +46 -47
- package/dist/lib/local-embeddings.js.map +1 -1
- package/dist/lib/local-gateway-client.d.ts.map +1 -1
- package/dist/lib/local-gateway-client.js +32 -26
- package/dist/lib/local-gateway-client.js.map +1 -1
- package/dist/lib/model-cache.d.ts +13 -0
- package/dist/lib/model-cache.d.ts.map +1 -1
- package/dist/lib/model-cache.js +26 -0
- package/dist/lib/model-cache.js.map +1 -1
- package/dist/lib/next-step.d.ts +2 -0
- package/dist/lib/next-step.d.ts.map +1 -1
- package/dist/lib/next-step.js +37 -16
- package/dist/lib/next-step.js.map +1 -1
- package/dist/lib/reason-extract.d.ts +35 -0
- package/dist/lib/reason-extract.d.ts.map +1 -0
- package/dist/lib/reason-extract.js +278 -0
- package/dist/lib/reason-extract.js.map +1 -0
- package/dist/lib/safe-config-write.d.ts +24 -1
- package/dist/lib/safe-config-write.d.ts.map +1 -1
- package/dist/lib/safe-config-write.js +113 -26
- package/dist/lib/safe-config-write.js.map +1 -1
- package/dist/lib/suppress-sqlite-warning.d.ts +13 -0
- package/dist/lib/suppress-sqlite-warning.d.ts.map +1 -0
- package/dist/lib/suppress-sqlite-warning.js +21 -0
- package/dist/lib/suppress-sqlite-warning.js.map +1 -0
- package/dist/lib/sync/spawn-background.d.ts +1 -1
- package/dist/lib/sync/spawn-background.d.ts.map +1 -1
- package/dist/lib/sync/spawn-background.js +1 -1
- package/dist/lib/sync/spawn-background.js.map +1 -1
- package/dist/lib/tar-lite.d.ts +18 -0
- package/dist/lib/tar-lite.d.ts.map +1 -0
- package/dist/lib/tar-lite.js +120 -0
- package/dist/lib/tar-lite.js.map +1 -0
- package/dist/lib/user-hooks.d.ts.map +1 -1
- package/dist/lib/user-hooks.js +13 -3
- package/dist/lib/user-hooks.js.map +1 -1
- package/dist/lib/verified-download.d.ts +19 -0
- package/dist/lib/verified-download.d.ts.map +1 -0
- package/dist/lib/verified-download.js +105 -0
- package/dist/lib/verified-download.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ALI-1749: the STATED reason behind a decision, pulled out of its own text with no model.
|
|
3
|
+
*
|
|
4
|
+
* `align ask` with no AI key lists related commits, and the first lines of a commit body are
|
|
5
|
+
* often not the reason (a dependabot bump opens with release-note boilerplate, a squash merge
|
|
6
|
+
* opens with a bullet list of sub-titles). This picks up to two sentences that SAY why.
|
|
7
|
+
*
|
|
8
|
+
* Rules, all deterministic and offline:
|
|
9
|
+
* - a sentence qualifies only through a causal marker (because, so that, to avoid, ...) or an
|
|
10
|
+
* explicit "Why:" / "Reason:" label. Strong markers outrank medium ones.
|
|
11
|
+
* - the text is the author's own words, only cleaned of markup. Nothing is paraphrased or invented.
|
|
12
|
+
* - what the importer embeds (Author/Date/Files changed/URL, see formatCommitAsText), commit
|
|
13
|
+
* trailers, "Fixes #N" lines, dependabot/renovate bodies, release-note dumps, tables, code,
|
|
14
|
+
* HTML comments and URLs are never reasons.
|
|
15
|
+
* - a sentence that only repeats the title is dropped.
|
|
16
|
+
* - null when nothing qualifies. A wrong reason is worse than none.
|
|
17
|
+
*/
|
|
18
|
+
/** Per-sentence cap, cut at a word boundary. A display choice, like EXCERPT_MAX_CHARS. */
|
|
19
|
+
export const REASON_MAX_CHARS = 180;
|
|
20
|
+
/** Reasons returned at most. */
|
|
21
|
+
const MAX_SENTENCES = 2;
|
|
22
|
+
/** Only the head of a body is read: a 280k-character line must not cost a second. */
|
|
23
|
+
const MAX_INPUT_CHARS = 6000;
|
|
24
|
+
/** Fewer words than this is a heading or a label, not a statement. */
|
|
25
|
+
const MIN_WORDS = 5;
|
|
26
|
+
// formatCommitAsText (src/lib/git.ts) appends: Author, Date, optional Files changed list, optional URL.
|
|
27
|
+
const IMPORTER_METADATA = /\n\nAuthor: [^\n]*\n\nDate: [^\n]*(?:\n\n(?:Files changed:|URL:)[\s\S]*)?$/;
|
|
28
|
+
/** The Author/Date/Files changed/URL block the git importer appends, removed. Anything else is returned unchanged. */
|
|
29
|
+
export function stripImporterMetadata(text) {
|
|
30
|
+
return text.replace(/\r\n?/g, '\n').replace(IMPORTER_METADATA, '');
|
|
31
|
+
}
|
|
32
|
+
const TRAILER = /^\s*(?:co-authored-by|signed-off-by|reviewed-by|reviewed-on|acked-by|tested-by|reported-by|suggested-by|helped-by|change-id|pr-url|cc|bug|jira|issue)\s*:/i;
|
|
33
|
+
const ISSUE_REF = '(?:#\\d+|gh-\\d+|[\\w.-]+\\/[\\w.-]+#\\d+|https?:\\/\\/\\S+)';
|
|
34
|
+
const FIXES_ONLY = new RegExp(`^\\s*(?:fix(?:e[sd])?|close[sd]?|resolve[sd]?|refs?|see|related to|part of)\\b:?\\s*${ISSUE_REF}(?:[\\s,;&]+(?:and\\s+)?${ISSUE_REF})*\\s*[.]?\\s*$`, 'i');
|
|
35
|
+
// Sections that start a dump of someone else's changes.
|
|
36
|
+
const DUMP_HEADING = /^\s{0,3}(?:#{1,6}\s*)?(?:\*\*)?(?:what'?s changed|release notes?|changelog|full changelog|commits|sourced from|bug fixes|features)\b|^\s{0,3}#{1,6}\s*\[?v?\d+\.\d+/im;
|
|
37
|
+
const BUMP_BODY = /^\s*(?:bumps?|updates?|upgrades?)\s+(?:\[?`?[\w@/.-]+`?\]?(?:\([^)]*\))?\s+)?(?:from|to)\b|this pr contains the following updates|^\s*dependabot will resolve/im;
|
|
38
|
+
const STRONG = [
|
|
39
|
+
/\bbecause\b/i, /\bso that\b/i, /\bin order to\b/i, /\bto avoid\b/i, /\bto prevent\b/i,
|
|
40
|
+
/\bdue to\b/i, /\bas a result\b/i, /\bwas causing\b/i, /\bcaused by\b/i,
|
|
41
|
+
// "since" as a cause starts a clause ("since it is...", "since Cargo finds..."); as a date or
|
|
42
|
+
// version it does not ("since node 16", "since pnpm/pnpm#1234", "since the first release").
|
|
43
|
+
/\bsince\s+(?:(?:the|it|its|we|they|this|that|there|a|an|our|these|those|you|i|all|both|each|every|no|not|only|one|most|many)\b(?!\s+(?:last|first|previous|beginning|start|release|version|v?\d))|[\w.:/#-]+\s+(?:is|are|was|were|has|have|had|does|do|can|could|will|would|must|should|may|might|needs?|uses?|finds?|takes?|lives?|stores?|ignores?|supports?|reads?|runs?|returns?|calls?|makes?|requires?|works?|handles?|treats?)\b)/i,
|
|
44
|
+
];
|
|
45
|
+
// These words DESCRIBE a change as often as they explain it ("X is no longer emitted", "use Y
|
|
46
|
+
// instead of Z"), so measured on fastify, vite and pnpm they were most of the noise. They count
|
|
47
|
+
// only next to a word that names a problem.
|
|
48
|
+
const MEDIUM = [
|
|
49
|
+
/\binstead of\b/i, /\brather than\b/i, /\bpreviously\b/i, /\bno longer\b/i, /\bto fix\b/i,
|
|
50
|
+
/\bregression\b/i, /\bdeprecated\b/i, /\breplaces\b/i, /\botherwise\b/i,
|
|
51
|
+
];
|
|
52
|
+
const PROBLEM = /\b(?:break(?:s|ing)?|broke|broken|fail(?:s|ed|ing|ure)?|crash(?:es|ed|ing)?|leak(?:s|ed|ing)?|incorrect(?:ly)?|wrong(?:ly)?|bugs?|errors?|slow(?:er|ly)?|hangs?|lead(?:s|ing)? to|result(?:s|ed|ing)? in|caus(?:e|es|ed|ing))\b/i;
|
|
53
|
+
const WEAK = [/\bsecurity\b/i, /\bperformance\b/i, /\bvulnerab\w*/i, /\bmemory leak\b/i, /\bcrash(?:es|ed)?\b/i];
|
|
54
|
+
const LABEL = /^(why|reason|rationale|motivation)\s*:\s*/i;
|
|
55
|
+
// A label followed by a placeholder or a pointer says nothing.
|
|
56
|
+
const EMPTY_LABEL_BODY = /^(?:n\/?a|none|see|tbd|todo|-)\b/i;
|
|
57
|
+
const NEGATED = /(?:\bnot|\bnever|n't)\s+(?:just\s+|only\s+|simply\s+)?because\b/i;
|
|
58
|
+
const FILE_TOKEN = /\b[\w-]+\.(?:ts|tsx|js|jsx|mjs|md|json|py|rs|go|txt|yml|yaml|css|html|toml|sh)\b/gi;
|
|
59
|
+
const CONVENTIONAL_PREFIX = /^[a-z]+(?:\([^)]*\))?!?:\s+/;
|
|
60
|
+
const BULLET = /^\s*(?:[*+-]|\d+[.)])\s+/;
|
|
61
|
+
const NOISE_SENTENCE = /^merge (?:pull request|branch|remote-tracking)\b|\b(?:sourced from|dependabot|renovate)\b/i;
|
|
62
|
+
// A removed URL leaves a marker, so a reason whose OBJECT was the link ("because of <url>") can be
|
|
63
|
+
// dropped rather than printed with a hole in it. Only real HTML tags are stripped: `<path>` in a
|
|
64
|
+
// sentence is placeholder text the author wrote.
|
|
65
|
+
const URL_HOLE = '\uE000';
|
|
66
|
+
const HTML_TAG = /<\/?(?:a|p|em|strong|b|i|u|code|pre|br|hr|h[1-6]|ul|ol|li|details|summary|blockquote|div|span|img|sup|sub|table|thead|tbody|tr|td|th)\b[^>]*>/gi;
|
|
67
|
+
const DANGLING_LINK = /(?:\b(?:because of|due to|of|to|by|with|from|for|on|in|and|or)|,)\s*\uE000/;
|
|
68
|
+
function stripMarkup(s) {
|
|
69
|
+
return s
|
|
70
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
71
|
+
.replace(HTML_TAG, ' ')
|
|
72
|
+
.replace(/https?:\/\/\S+/g, ` ${URL_HOLE} `)
|
|
73
|
+
.replace(/(\*\*|__)(.+?)\1/g, '$2')
|
|
74
|
+
.replace(/`([^`]*)`/g, '$1')
|
|
75
|
+
.replace(/(^|\s)[*_]([^*_\s][^*_]*)[*_](?=\s|[.,;:!?]|$)/g, '$1$2')
|
|
76
|
+
.replace(/\s+/g, ' ')
|
|
77
|
+
.trim();
|
|
78
|
+
}
|
|
79
|
+
function words(s) {
|
|
80
|
+
return s.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
81
|
+
}
|
|
82
|
+
function isMostlyEnglish(s) {
|
|
83
|
+
const letters = s.match(/\p{L}/gu)?.length ?? 0;
|
|
84
|
+
const ascii = s.match(/[A-Za-z]/g)?.length ?? 0;
|
|
85
|
+
return letters > 0 && ascii / letters >= 0.8;
|
|
86
|
+
}
|
|
87
|
+
function repeatsTitle(sentence, titleWords, titleBodyWords) {
|
|
88
|
+
const sw = words(sentence);
|
|
89
|
+
if (!sw.length || !titleWords.length)
|
|
90
|
+
return false;
|
|
91
|
+
const inTitle = new Set(titleWords);
|
|
92
|
+
if (sw.filter((w) => inTitle.has(w)).length / sw.length >= 0.8)
|
|
93
|
+
return true;
|
|
94
|
+
// The importer cuts a long title at 80 characters, so a body that opens with the whole subject
|
|
95
|
+
// continues past where the title stops. Same words up to the cut, last (cut) word aside.
|
|
96
|
+
const head = titleBodyWords.slice(0, -1);
|
|
97
|
+
return head.length >= 3 && head.every((w, i) => sw[i] === w);
|
|
98
|
+
}
|
|
99
|
+
function score(sentence, labelled) {
|
|
100
|
+
let n = labelled ? 3 : 0;
|
|
101
|
+
if (STRONG.some((re) => re.test(sentence)))
|
|
102
|
+
n = Math.max(n, 3);
|
|
103
|
+
else if (MEDIUM.some((re) => re.test(sentence)) && PROBLEM.test(sentence.replace(/breaking change/gi, '')))
|
|
104
|
+
n = Math.max(n, 2);
|
|
105
|
+
if (n > 0 && WEAK.some((re) => re.test(sentence)))
|
|
106
|
+
n += 0.5;
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
function cap(sentence) {
|
|
110
|
+
if (sentence.length <= REASON_MAX_CHARS)
|
|
111
|
+
return sentence;
|
|
112
|
+
const head = sentence.slice(0, REASON_MAX_CHARS);
|
|
113
|
+
const cut = sentence[REASON_MAX_CHARS] === ' ' ? head : head.replace(/\s*\S*$/, '');
|
|
114
|
+
return `${(cut.trimEnd() || head).replace(/[,;:\-\s]+$/, '')}...`;
|
|
115
|
+
}
|
|
116
|
+
function firstMarker(sentence) {
|
|
117
|
+
let best = null;
|
|
118
|
+
for (const re of [...STRONG, ...MEDIUM]) {
|
|
119
|
+
const m = re.exec(sentence);
|
|
120
|
+
if (m && (!best || m.index < best.index))
|
|
121
|
+
best = { index: m.index, end: m.index + m[0].length };
|
|
122
|
+
}
|
|
123
|
+
return best;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The text to show for a qualifying sentence. A long one is cut at the cap, which can hide the very
|
|
127
|
+
* marker that qualified it (measured: a sentence kept for a "because" at character 230 printed only
|
|
128
|
+
* its first 180, with no reason in it). A tail window starting mid-sentence was tried and read as a
|
|
129
|
+
* fragment ("...so that half of the fix is v12 only."), so a sentence whose marker lies past the cap
|
|
130
|
+
* is dropped.
|
|
131
|
+
*/
|
|
132
|
+
function fit(sentence, labelled) {
|
|
133
|
+
if (sentence.length <= REASON_MAX_CHARS || labelled)
|
|
134
|
+
return cap(sentence);
|
|
135
|
+
const m = firstMarker(sentence);
|
|
136
|
+
return !m || m.end <= REASON_MAX_CHARS ? cap(sentence) : null;
|
|
137
|
+
}
|
|
138
|
+
/** Units are paragraphs and bullet items. Hard-wrapped lines of one unit are joined. */
|
|
139
|
+
function toUnits(text) {
|
|
140
|
+
const units = [];
|
|
141
|
+
let cur = [];
|
|
142
|
+
let curBullet = false;
|
|
143
|
+
const flush = () => {
|
|
144
|
+
if (cur.length)
|
|
145
|
+
units.push({ text: cur.join(' '), bullet: curBullet });
|
|
146
|
+
cur = [];
|
|
147
|
+
curBullet = false;
|
|
148
|
+
};
|
|
149
|
+
for (const raw of text.split('\n')) {
|
|
150
|
+
const line = raw.replace(/^\s*>+\s?/, '');
|
|
151
|
+
if (!line.trim()) {
|
|
152
|
+
flush();
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (/^\s*#{1,6}\s/.test(line) || /^\s*\|/.test(line) || /^\s*[-=*_|: ]{3,}\s*$/.test(line)) {
|
|
156
|
+
flush();
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (TRAILER.test(line) || FIXES_ONLY.test(line)) {
|
|
160
|
+
flush();
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (/^\s*(?=[0-9a-f]*\d)[0-9a-f]{7,40}\b/.test(line)) {
|
|
164
|
+
flush();
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (BULLET.test(line)) {
|
|
168
|
+
flush();
|
|
169
|
+
curBullet = true;
|
|
170
|
+
cur.push(line.replace(BULLET, ''));
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
// Short unpunctuated lines are list entries, not a wrapped paragraph: a wrapped line runs to the
|
|
174
|
+
// width and the next one continues in lower case.
|
|
175
|
+
const prev = cur[cur.length - 1];
|
|
176
|
+
if (prev !== undefined && prev.length < 60 && !/[.!?:;,]$/.test(prev) && /^[A-Z]/.test(line.trim()))
|
|
177
|
+
flush();
|
|
178
|
+
cur.push(line.trim());
|
|
179
|
+
}
|
|
180
|
+
flush();
|
|
181
|
+
return units;
|
|
182
|
+
}
|
|
183
|
+
const SENTENCE_SPLIT = /(?<=[.!?])(?<!\b(?:e\.g|i\.e|etc|vs|cf|[Mm]r|[Mm]rs|[Mm]s|[Dd]r|Inc|Ltd|[Jj]r|[Ss]r|St)\.)\s+(?=[A-Z0-9"'(`[])/;
|
|
184
|
+
export function extractReason(title, body) {
|
|
185
|
+
if (!body)
|
|
186
|
+
return null;
|
|
187
|
+
let text = stripImporterMetadata(body).slice(0, MAX_INPUT_CHARS);
|
|
188
|
+
// The summary opens with its own title; the caller already prints it.
|
|
189
|
+
const flatTitle = (title ?? '').replace(/\s+/g, ' ').trim();
|
|
190
|
+
const trimmed = text.trimStart();
|
|
191
|
+
const nl = trimmed.indexOf('\n');
|
|
192
|
+
const firstLine = (nl < 0 ? trimmed : trimmed.slice(0, nl)).replace(/\s+/g, ' ').trim();
|
|
193
|
+
if (flatTitle && firstLine === flatTitle)
|
|
194
|
+
text = nl < 0 ? '' : trimmed.slice(nl);
|
|
195
|
+
// Someone else's changes: bumps and release notes say what a dependency did, not why we took it.
|
|
196
|
+
if (BUMP_BODY.test(text))
|
|
197
|
+
return null;
|
|
198
|
+
const dump = DUMP_HEADING.exec(text);
|
|
199
|
+
if (dump)
|
|
200
|
+
text = text.slice(0, dump.index);
|
|
201
|
+
text = text.replace(/<!--[\s\S]*?(?:-->|$)/g, '\n').replace(/^\s*(```|~~~)[\s\S]*?(?:^\s*\1|$(?![\s\S]))/gm, '\n');
|
|
202
|
+
const titleWords = words(flatTitle);
|
|
203
|
+
const titleBodyWords = words(flatTitle.replace(CONVENTIONAL_PREFIX, ''));
|
|
204
|
+
const scored = [];
|
|
205
|
+
let pos = 0;
|
|
206
|
+
for (const unit of toUnits(text)) {
|
|
207
|
+
let t = stripMarkup(unit.text);
|
|
208
|
+
let labelled = false;
|
|
209
|
+
const label = LABEL.exec(t);
|
|
210
|
+
if (label) {
|
|
211
|
+
labelled = true;
|
|
212
|
+
t = t.slice(label[0].length);
|
|
213
|
+
if (EMPTY_LABEL_BODY.test(t))
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
// A squashed sub-commit title (a "* " list item, or "fix(router): ...") says what changed. Only a strong marker makes it a reason.
|
|
217
|
+
const conventional = CONVENTIONAL_PREFIX.test(t);
|
|
218
|
+
if (conventional)
|
|
219
|
+
t = t.replace(CONVENTIONAL_PREFIX, '');
|
|
220
|
+
const subjectLike = conventional || unit.bullet;
|
|
221
|
+
for (const sentence of t.split(SENTENCE_SPLIT)) {
|
|
222
|
+
let s = sentence.trim();
|
|
223
|
+
pos += 1;
|
|
224
|
+
if (DANGLING_LINK.test(s))
|
|
225
|
+
continue;
|
|
226
|
+
s = s.replace(/\s*\uE000\s*([.,;:!?])/g, '$1').replace(/\s*\uE000\s*/g, ' ').trim();
|
|
227
|
+
if (words(s).length < MIN_WORDS || !isMostlyEnglish(s) || NOISE_SENTENCE.test(s) || repeatsTitle(s, titleWords, titleBodyWords))
|
|
228
|
+
continue;
|
|
229
|
+
if (/\?$/.test(s))
|
|
230
|
+
continue;
|
|
231
|
+
// Score what the author asserts: not text in quotation marks (someone else's claim), not a
|
|
232
|
+
// file name that happens to contain a marker word, not a negated "because".
|
|
233
|
+
const asserted = s.replace(/"[^"]*"|\u201c[^\u201d]*\u201d/g, ' ').replace(FILE_TOKEN, ' ');
|
|
234
|
+
if (NEGATED.test(asserted))
|
|
235
|
+
continue;
|
|
236
|
+
const sc = score(asserted, labelled);
|
|
237
|
+
if (sc < 2 || (subjectLike && sc < 3))
|
|
238
|
+
continue;
|
|
239
|
+
const shown = fit(s, labelled);
|
|
240
|
+
if (shown)
|
|
241
|
+
scored.push({ pos, score: sc, text: shown });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (!scored.length)
|
|
245
|
+
return null;
|
|
246
|
+
const picked = [...scored].sort((a, b) => b.score - a.score || a.pos - b.pos).slice(0, MAX_SENTENCES);
|
|
247
|
+
return picked.sort((a, b) => a.pos - b.pos).map((p) => p.text).join(' ');
|
|
248
|
+
}
|
|
249
|
+
const COMMIT_SHA = /(?:\/commit\/|^git:\/\/commit\/)([0-9a-f]{7,40})(?:[/?#]|$)/;
|
|
250
|
+
/** The commit sha a commit-backed decision points at, from its source_url. Undefined for anything else. */
|
|
251
|
+
export function commitShaOf(sourceUrl) {
|
|
252
|
+
return sourceUrl ? COMMIT_SHA.exec(sourceUrl)?.[1] : undefined;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Additive shaping for search results: each row gains `reason` (string, or an explicit null so an
|
|
256
|
+
* agent can tell "no stated reason" from "not provided") and, with `sha`, the commit sha when the
|
|
257
|
+
* row's source_url is a commit. No existing field is changed. Pure: the input is not mutated.
|
|
258
|
+
*/
|
|
259
|
+
export function withReasons(payload, opts = {}) {
|
|
260
|
+
const rows = payload?.results;
|
|
261
|
+
if (!Array.isArray(rows))
|
|
262
|
+
return payload;
|
|
263
|
+
return {
|
|
264
|
+
...payload,
|
|
265
|
+
results: rows.map((row) => {
|
|
266
|
+
if (row === null || typeof row !== 'object' || Array.isArray(row))
|
|
267
|
+
return row;
|
|
268
|
+
const r = row;
|
|
269
|
+
const sha = opts.sha ? commitShaOf(typeof r['source_url'] === 'string' ? r['source_url'] : undefined) : undefined;
|
|
270
|
+
return {
|
|
271
|
+
...r,
|
|
272
|
+
...(sha && !('sha' in r) ? { sha } : {}),
|
|
273
|
+
reason: 'reason' in r ? r['reason'] : extractReason(String(r['title'] ?? ''), typeof r['summary'] === 'string' ? r['summary'] : null),
|
|
274
|
+
};
|
|
275
|
+
}),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=reason-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reason-extract.js","sourceRoot":"","sources":["../../src/lib/reason-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmodSync, copyFileSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { chmodSync, copyFileSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
export declare const BACKUP_SUFFIX = ".align-backup";
|
|
3
3
|
export type SafeWriteStatus = 'written' | 'unchanged' | 'declined' | 'symlink';
|
|
4
4
|
/** The fs calls the writer makes. A parameter so a test can change the file mid-write. */
|
|
@@ -13,6 +13,7 @@ export interface SafeFs {
|
|
|
13
13
|
unlinkSync: typeof unlinkSync;
|
|
14
14
|
mkdirSync: typeof mkdirSync;
|
|
15
15
|
readdirSync: typeof readdirSync;
|
|
16
|
+
rmdirSync: typeof rmdirSync;
|
|
16
17
|
}
|
|
17
18
|
type Json = Record<string, unknown>;
|
|
18
19
|
/** One value or array element align put in a JSON config. Hashes only, never the value. */
|
|
@@ -48,6 +49,11 @@ export interface WrittenConfig {
|
|
|
48
49
|
/** 'made': align made the backup. 'foreign': one was already there and is not trusted. 'none': no original to keep. */
|
|
49
50
|
backup: 'made' | 'foreign' | 'none';
|
|
50
51
|
backupSha256?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Where the backup lives when it is not beside the file (project files). Snapshots are
|
|
54
|
+
* `<backupAt>.<n>`. Absent: `<file>.align-backup`.
|
|
55
|
+
*/
|
|
56
|
+
backupAt?: string;
|
|
51
57
|
owned?: OwnedItem[];
|
|
52
58
|
/**
|
|
53
59
|
* Extra copies of the file taken just before a write that found it changed by someone else
|
|
@@ -57,6 +63,11 @@ export interface WrittenConfig {
|
|
|
57
63
|
n: number;
|
|
58
64
|
sha256: string;
|
|
59
65
|
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Directories align's first write made on the way to this file (outermost first). Undo removes
|
|
68
|
+
* each one only if it is empty by then, so a directory that held anything else stays.
|
|
69
|
+
*/
|
|
70
|
+
createdDirs?: string[];
|
|
60
71
|
/** Text files: the managed block align wrote. */
|
|
61
72
|
block?: {
|
|
62
73
|
start: string;
|
|
@@ -98,12 +109,24 @@ export interface SafeWriteOptions {
|
|
|
98
109
|
};
|
|
99
110
|
/** TOML only: the table (e.g. ['mcp_servers', 'align-local']) the managed block holds. */
|
|
100
111
|
tomlTable?: string[];
|
|
112
|
+
/**
|
|
113
|
+
* A file in the user's repo, not in an agent's private config: new files and directories get the
|
|
114
|
+
* default mode (the umask decides) instead of owner-only, since a team shares them.
|
|
115
|
+
*/
|
|
116
|
+
projectFile?: boolean;
|
|
101
117
|
/** The tail of the invalid-JSON message: what to do about it. */
|
|
102
118
|
invalidJsonAdvice?: string;
|
|
103
119
|
}
|
|
104
120
|
export declare class SafeWriteConflictError extends Error {
|
|
105
121
|
constructor(file: string);
|
|
106
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Where a PROJECT file's backup goes: `<state home>/align-cli/undo-backups/<sha of the full path>/<name>.align-backup`.
|
|
125
|
+
* Out of the repo on purpose, so nothing shows in `git status`. Keyed on the full path, the same key
|
|
126
|
+
* the ledger uses, so a moved repo loses both together and a same-named file elsewhere never collides.
|
|
127
|
+
* XDG_STATE_HOME is honoured; %LOCALAPPDATA% on Windows (case-folded there, as its paths are).
|
|
128
|
+
*/
|
|
129
|
+
export declare function projectBackupBase(file: string): string;
|
|
107
130
|
/** The text form. `compute` gets the current text (null when missing) and returns the new text, or undefined to leave it. */
|
|
108
131
|
export declare function safeWriteText(file: string, compute: (current: string | null) => string | undefined, opts?: SafeWriteOptions): SafeWriteStatus;
|
|
109
132
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-config-write.d.ts","sourceRoot":"","sources":["../../src/lib/safe-config-write.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,YAAY,EACZ,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"safe-config-write.d.ts","sourceRoot":"","sources":["../../src/lib/safe-config-write.ts"],"names":[],"mappings":"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"}
|