@aligndottech/cli 0.65.2 → 0.66.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 (50) hide show
  1. package/README.md +28 -1
  2. package/dist/commands/check.d.ts.map +1 -1
  3. package/dist/commands/check.js +12 -7
  4. package/dist/commands/check.js.map +1 -1
  5. package/dist/commands/mcp.d.ts +37 -0
  6. package/dist/commands/mcp.d.ts.map +1 -1
  7. package/dist/commands/mcp.js +16 -1
  8. package/dist/commands/mcp.js.map +1 -1
  9. package/dist/commands/registry.d.ts.map +1 -1
  10. package/dist/commands/registry.js +2 -0
  11. package/dist/commands/registry.js.map +1 -1
  12. package/dist/commands/stale.d.ts +3 -0
  13. package/dist/commands/stale.d.ts.map +1 -0
  14. package/dist/commands/stale.js +43 -0
  15. package/dist/commands/stale.js.map +1 -0
  16. package/dist/lib/advisory-verdict.d.ts +2 -0
  17. package/dist/lib/advisory-verdict.d.ts.map +1 -1
  18. package/dist/lib/advisory-verdict.js +2 -0
  19. package/dist/lib/advisory-verdict.js.map +1 -1
  20. package/dist/lib/blame-attribution.d.ts +2 -0
  21. package/dist/lib/blame-attribution.d.ts.map +1 -1
  22. package/dist/lib/blame-attribution.js +1 -1
  23. package/dist/lib/blame-attribution.js.map +1 -1
  24. package/dist/lib/decision-links.d.ts +24 -0
  25. package/dist/lib/decision-links.d.ts.map +1 -1
  26. package/dist/lib/decision-links.js +69 -1
  27. package/dist/lib/decision-links.js.map +1 -1
  28. package/dist/lib/gateway-client.d.ts +6 -0
  29. package/dist/lib/gateway-client.d.ts.map +1 -1
  30. package/dist/lib/gateway-client.js.map +1 -1
  31. package/dist/lib/mcp-timeline-tools.d.ts.map +1 -1
  32. package/dist/lib/mcp-timeline-tools.js +5 -3
  33. package/dist/lib/mcp-timeline-tools.js.map +1 -1
  34. package/dist/lib/stale-code.d.ts +88 -0
  35. package/dist/lib/stale-code.d.ts.map +1 -0
  36. package/dist/lib/stale-code.js +135 -0
  37. package/dist/lib/stale-code.js.map +1 -0
  38. package/dist/lib/stale-links.d.ts +27 -0
  39. package/dist/lib/stale-links.d.ts.map +1 -0
  40. package/dist/lib/stale-links.js +58 -0
  41. package/dist/lib/stale-links.js.map +1 -0
  42. package/dist/lib/stale-render.d.ts +6 -0
  43. package/dist/lib/stale-render.d.ts.map +1 -0
  44. package/dist/lib/stale-render.js +47 -0
  45. package/dist/lib/stale-render.js.map +1 -0
  46. package/dist/lib/stale-run.d.ts +22 -0
  47. package/dist/lib/stale-run.d.ts.map +1 -0
  48. package/dist/lib/stale-run.js +17 -0
  49. package/dist/lib/stale-run.js.map +1 -0
  50. package/package.json +1 -1
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Code still built on a replaced decision.
3
+ *
4
+ * For each `supersedes` link (new replaces old) take the OLD decision's source, find the commit it
5
+ * names, list the files that commit touched, and `git blame` those files on the default branch. A line
6
+ * whose blame is still that commit has not been rewritten since the decision it came from was replaced.
7
+ * That is a fact about history, not a verdict: the line may be fine. Everything is local git, no model.
8
+ */
9
+ export interface StaleLink {
10
+ oldId: string;
11
+ oldTitle: string;
12
+ oldSourceUrl: string | null;
13
+ newId: string;
14
+ newTitle: string;
15
+ newSourceUrl: string | null;
16
+ confidence: number;
17
+ /** A person confirmed the link. False means a classifier wrote it. */
18
+ humanConfirmed: boolean;
19
+ }
20
+ export interface StaleOptions {
21
+ cwd: string;
22
+ /** Also use links a classifier wrote and no person confirmed. Default false. */
23
+ includeAi?: boolean;
24
+ /** Most files blamed for one old decision. */
25
+ maxFilesPerDecision?: number;
26
+ /** Wall-clock budget for the whole run. */
27
+ budgetMs?: number;
28
+ }
29
+ export type LineRange = [start: number, end: number];
30
+ export interface StaleFileHit {
31
+ file: string;
32
+ lines: number;
33
+ ranges: LineRange[];
34
+ }
35
+ export interface StaleFinding {
36
+ replaced: {
37
+ id: string;
38
+ title: string;
39
+ sourceUrl: string | null;
40
+ };
41
+ replacedBy: {
42
+ id: string;
43
+ title: string;
44
+ sourceUrl: string | null;
45
+ };
46
+ commit: string;
47
+ confirmation: 'confirmed' | 'ai-suggested';
48
+ /** Present for an AI-suggested link, so the reader can weigh it. */
49
+ confidence?: number;
50
+ files: StaleFileHit[];
51
+ totalLines: number;
52
+ }
53
+ export interface StaleSkip {
54
+ decisionId: string;
55
+ title: string;
56
+ reason: string;
57
+ }
58
+ export interface StaleReport {
59
+ findings: StaleFinding[];
60
+ skipped: StaleSkip[];
61
+ /** Per-decision file cap hits: old decisions with more touched files than the cap. */
62
+ filesCapped: number;
63
+ budgetExceeded: boolean;
64
+ linksConsidered: number;
65
+ /** AI-suggested links left out because --include-ai was not set. */
66
+ aiExcluded: number;
67
+ ref: string;
68
+ }
69
+ export declare class StaleError extends Error {
70
+ }
71
+ export declare const DEFAULT_MAX_FILES = 40;
72
+ export declare const DEFAULT_BUDGET_MS = 90000;
73
+ type Git = (args: string[]) => Promise<string>;
74
+ /** The branch decisions land on: origin/HEAD, else main, else master, else the checked-out HEAD. */
75
+ export declare function defaultRef(git: Git): Promise<string>;
76
+ type Source = {
77
+ kind: 'commit';
78
+ sha: string;
79
+ repo: string;
80
+ } | {
81
+ kind: 'pr';
82
+ number: string;
83
+ repo: string;
84
+ };
85
+ export declare function parseSource(url: string | null): Source | null;
86
+ export declare function findStaleCode(links: StaleLink[], opts: StaleOptions): Promise<StaleReport>;
87
+ export {};
88
+ //# sourceMappingURL=stale-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-code.d.ts","sourceRoot":"","sources":["../../src/lib/stale-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,135 @@
1
+ import { execa } from 'execa';
2
+ import { githubRepoOf, isProseOrGeneratedFile } from './blame-attribution.js';
3
+ export class StaleError extends Error {
4
+ }
5
+ export const DEFAULT_MAX_FILES = 40;
6
+ export const DEFAULT_BUDGET_MS = 90_000;
7
+ const GIT_TIMEOUT_MS = 30_000;
8
+ function gitIn(cwd) {
9
+ return async (args) => (await execa('git', args, { cwd, timeout: GIT_TIMEOUT_MS, stdin: 'ignore', maxBuffer: 256 * 1024 * 1024 })).stdout;
10
+ }
11
+ const ok = (git, args) => git(args).then(() => true, () => false);
12
+ /** The branch decisions land on: origin/HEAD, else main, else master, else the checked-out HEAD. */
13
+ export async function defaultRef(git) {
14
+ const head = await git(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']).catch(() => '');
15
+ if (head.trim() && await ok(git, ['rev-parse', '--verify', '-q', head.trim()]))
16
+ return head.trim();
17
+ for (const name of ['main', 'master'])
18
+ if (await ok(git, ['rev-parse', '--verify', '-q', name]))
19
+ return name;
20
+ return 'HEAD';
21
+ }
22
+ export function parseSource(url) {
23
+ if (!url)
24
+ return null;
25
+ const m = /^https?:\/\/github\.com\/([^/\s]+)\/([^/\s]+)\/(commit|pull)\/([0-9a-f]+|\d+)(?:[/?#].*)?$/i.exec(url.trim());
26
+ if (!m)
27
+ return null;
28
+ const repo = `${m[1]}/${m[2]}`.toLowerCase();
29
+ if (m[3] === 'commit' && /^[0-9a-f]{7,40}$/i.test(m[4]))
30
+ return { kind: 'commit', sha: m[4].toLowerCase(), repo };
31
+ if (m[3] === 'pull' && /^\d+$/.test(m[4]))
32
+ return { kind: 'pr', number: m[4], repo };
33
+ return null;
34
+ }
35
+ async function blameRanges(git, ref, file, sha) {
36
+ const out = await git(['blame', '-w', '-M', '--line-porcelain', ref, '--', file]);
37
+ const lines = [];
38
+ for (const row of out.split('\n')) {
39
+ const m = /^([0-9a-f]{40}) \d+ (\d+)/.exec(row);
40
+ if (m && m[1] === sha)
41
+ lines.push(Number(m[2]));
42
+ }
43
+ const ranges = [];
44
+ for (const n of lines) {
45
+ const last = ranges[ranges.length - 1];
46
+ if (last && last[1] === n - 1)
47
+ last[1] = n;
48
+ else
49
+ ranges.push([n, n]);
50
+ }
51
+ return ranges;
52
+ }
53
+ export async function findStaleCode(links, opts) {
54
+ const git = gitIn(opts.cwd);
55
+ if (!await ok(git, ['rev-parse', '--git-dir']))
56
+ throw new StaleError('This is not a git repository. Run align stale inside the repository to check.');
57
+ if ((await git(['rev-parse', '--is-shallow-repository'])).trim() === 'true') {
58
+ throw new StaleError('This is a shallow clone, so the commits that wrote the old code are missing and every result would read as "nothing found". Run `git fetch --unshallow` and try again.');
59
+ }
60
+ const ref = await defaultRef(git);
61
+ const origin = githubRepoOf(await git(['remote', 'get-url', 'origin']).catch(() => ''));
62
+ const maxFiles = opts.maxFilesPerDecision ?? DEFAULT_MAX_FILES;
63
+ const deadline = Date.now() + (opts.budgetMs ?? DEFAULT_BUDGET_MS);
64
+ const usable = opts.includeAi ? links : links.filter((l) => l.humanConfirmed);
65
+ const report = {
66
+ findings: [], skipped: [], filesCapped: 0, budgetExceeded: false,
67
+ linksConsidered: usable.length, aiExcluded: links.length - usable.length, ref,
68
+ };
69
+ const hitsBySha = new Map();
70
+ for (const link of usable) {
71
+ const skip = (reason) => report.skipped.push({ decisionId: link.oldId, title: link.oldTitle, reason });
72
+ if (Date.now() > deadline) {
73
+ report.budgetExceeded = true;
74
+ skip('time budget used up before this decision');
75
+ continue;
76
+ }
77
+ const src = parseSource(link.oldSourceUrl);
78
+ if (!src) {
79
+ skip('its source is not a GitHub commit or pull request link');
80
+ continue;
81
+ }
82
+ if (origin && src.repo !== origin) {
83
+ skip(`its source is in ${src.repo}, not this repository`);
84
+ continue;
85
+ }
86
+ let sha = null;
87
+ if (src.kind === 'commit') {
88
+ sha = (await git(['rev-parse', '--verify', '-q', `${src.sha}^{commit}`]).catch(() => '')).trim() || null;
89
+ if (!sha) {
90
+ skip('its commit is not in this clone');
91
+ continue;
92
+ }
93
+ }
94
+ else {
95
+ sha = (await git(['log', ref, '-F', '--grep', `(#${src.number})`, '-1', '--format=%H']).catch(() => '')).trim() || null;
96
+ if (!sha) {
97
+ skip(`no commit on ${ref} mentions (#${src.number})`);
98
+ continue;
99
+ }
100
+ }
101
+ let hits = hitsBySha.get(sha);
102
+ if (!hits) {
103
+ const touched = (await git(['show', '--name-only', '--format=', sha])).split('\n').filter((f) => f && !isProseOrGeneratedFile(f));
104
+ if (touched.length > maxFiles)
105
+ report.filesCapped++;
106
+ hits = [];
107
+ for (const file of touched.slice(0, maxFiles)) {
108
+ if (Date.now() > deadline) {
109
+ report.budgetExceeded = true;
110
+ break;
111
+ }
112
+ // A file the commit touched may be gone or renamed on the default branch: nothing left to blame there.
113
+ const ranges = await blameRanges(git, ref, file, sha).catch(() => []);
114
+ const lines = ranges.reduce((n, [s, e]) => n + (e - s + 1), 0);
115
+ if (lines > 0)
116
+ hits.push({ file, lines, ranges });
117
+ }
118
+ hitsBySha.set(sha, hits);
119
+ }
120
+ if (!hits.length)
121
+ continue;
122
+ report.findings.push({
123
+ replaced: { id: link.oldId, title: link.oldTitle, sourceUrl: link.oldSourceUrl },
124
+ replacedBy: { id: link.newId, title: link.newTitle, sourceUrl: link.newSourceUrl },
125
+ commit: sha,
126
+ confirmation: link.humanConfirmed ? 'confirmed' : 'ai-suggested',
127
+ ...(link.humanConfirmed ? {} : { confidence: link.confidence }),
128
+ files: hits,
129
+ totalLines: hits.reduce((n, h) => n + h.lines, 0),
130
+ });
131
+ }
132
+ report.findings.sort((a, b) => b.totalLines - a.totalLines);
133
+ return report;
134
+ }
135
+ //# sourceMappingURL=stale-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-code.js","sourceRoot":"","sources":["../../src/lib/stale-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { EnvironmentConfig } from './config.js';
2
+ import type { DecisionLink } from './gateway-client.js';
3
+ import type { StaleLink } from './stale-code.js';
4
+ /** The slice of the gateway client this needs; the cloud client satisfies it, the local one does not. */
5
+ export interface LinkSourceClient {
6
+ listDecisionLinks(filters?: {
7
+ relation?: string;
8
+ }): Promise<{
9
+ links: DecisionLink[];
10
+ total_count: number;
11
+ }>;
12
+ getDecision(id: string): Promise<{
13
+ source_url?: string | null;
14
+ }>;
15
+ }
16
+ export interface LoadedLinks {
17
+ links: StaleLink[];
18
+ /** Why the set is smaller than expected, to print instead of an empty result. */
19
+ notes: string[];
20
+ }
21
+ export declare const LOCAL_EMPTY_NOTE: string;
22
+ /** A person's `align mark ... replaces` writes its link under a `mark:` id; every other local edge is the classifier's. */
23
+ export declare function loadLocalLinks(dbPath: string): LoadedLinks;
24
+ /** Link rows from the team graph, plus each endpoint's source URL from /snapshots/:id. A link is a person's only when the gateway names a non-"ai" method. */
25
+ export declare function loadTeamLinks(client: LinkSourceClient): Promise<LoadedLinks>;
26
+ export declare function loadStaleLinks(env: EnvironmentConfig, client: LinkSourceClient | null): Promise<LoadedLinks>;
27
+ //# sourceMappingURL=stale-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-links.d.ts","sourceRoot":"","sources":["../../src/lib/stale-links.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,yGAAyG;AACzG,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5G,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,iFAAiF;IACjF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,eAAO,MAAM,gBAAgB,QAE0G,CAAC;AAExI,2HAA2H;AAC3H,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAkB1D;AAED,8JAA8J;AAC9J,wBAAsB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkBlF;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAIlH"}
@@ -0,0 +1,58 @@
1
+ import { createLocalDb } from './local-db.js';
2
+ export const LOCAL_EMPTY_NOTE = 'The local graph has no supersedes links. A keyless git import records only "relates" links between commits, ' +
3
+ 'so nothing says which decision replaced which. Record one with `align mark <new> replaces <old>`, or use a team graph (--env prod).';
4
+ /** A person's `align mark ... replaces` writes its link under a `mark:` id; every other local edge is the classifier's. */
5
+ export function loadLocalLinks(dbPath) {
6
+ const db = createLocalDb(dbPath);
7
+ try {
8
+ const rows = db.listLinks({ relation: 'supersedes' });
9
+ const links = rows.flatMap((r) => {
10
+ const nu = db.getDecisionById(r.sourceId);
11
+ const old = db.getDecisionById(r.targetId);
12
+ if (!nu || !old)
13
+ return [];
14
+ return [{
15
+ oldId: old.id, oldTitle: old.title, oldSourceUrl: old.sourceUrl,
16
+ newId: nu.id, newTitle: nu.title, newSourceUrl: nu.sourceUrl,
17
+ confidence: r.confidence, humanConfirmed: r.id.startsWith('mark:'),
18
+ }];
19
+ });
20
+ return { links, notes: links.length ? [] : [LOCAL_EMPTY_NOTE] };
21
+ }
22
+ finally {
23
+ db.close();
24
+ }
25
+ }
26
+ /** Link rows from the team graph, plus each endpoint's source URL from /snapshots/:id. A link is a person's only when the gateway names a non-"ai" method. */
27
+ export async function loadTeamLinks(client) {
28
+ const { links: rows, total_count } = await client.listDecisionLinks({ relation: 'supersedes' });
29
+ const notes = [];
30
+ if (total_count > rows.length)
31
+ notes.push(`Read the first ${rows.length} of ${total_count} supersedes links.`);
32
+ const urls = new Map();
33
+ const sourceOf = (id) => {
34
+ let p = urls.get(id);
35
+ if (!p) {
36
+ p = client.getDecision(id).then((d) => d.source_url ?? null, () => null);
37
+ urls.set(id, p);
38
+ }
39
+ return p;
40
+ };
41
+ const links = await Promise.all(rows.map(async (l) => ({
42
+ oldId: l.to_decision.id, oldTitle: l.to_decision.title, oldSourceUrl: await sourceOf(l.to_decision.id),
43
+ newId: l.from_decision.id, newTitle: l.from_decision.title, newSourceUrl: await sourceOf(l.from_decision.id),
44
+ confidence: Number(l.confidence ?? 0),
45
+ humanConfirmed: typeof l.detection_method === 'string' && l.detection_method !== '' && l.detection_method !== 'ai',
46
+ })));
47
+ if (!links.length)
48
+ notes.push('The team graph has no supersedes links.');
49
+ return { links, notes };
50
+ }
51
+ export async function loadStaleLinks(env, client) {
52
+ if (env.mode === 'local-embedded')
53
+ return loadLocalLinks(env.localDbPath ?? ':memory:');
54
+ if (!client)
55
+ throw new Error('No gateway client for this environment.');
56
+ return loadTeamLinks(client);
57
+ }
58
+ //# sourceMappingURL=stale-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-links.js","sourceRoot":"","sources":["../../src/lib/stale-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { StaleFinding, StaleReport } from './stale-code.js';
2
+ /** The wording the command and the MCP tool both use: never "wrong", only built on a replaced decision. */
3
+ export declare function renderFinding(f: StaleFinding): string[];
4
+ export declare function reportNotes(r: StaleReport, extra: string[], includeAi: boolean): string[];
5
+ export declare function renderReport(r: StaleReport, extra: string[], includeAi: boolean): string;
6
+ //# sourceMappingURL=stale-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-render.d.ts","sourceRoot":"","sources":["../../src/lib/stale-render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAMjE,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,CAWvD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,EAAE,CASzF;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CAWxF"}
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
3
+ const rangeText = (r) => (r[0] === r[1] ? `${r[0]}` : `${r[0]}-${r[1]}`);
4
+ const shortRanges = (rs) => { const t = rs.slice(0, 6).map(rangeText).join(', '); return rs.length > 6 ? `${t}, +${rs.length - 6} more` : t; };
5
+ /** The wording the command and the MCP tool both use: never "wrong", only built on a replaced decision. */
6
+ export function renderFinding(f) {
7
+ const out = [
8
+ `${plural(f.totalLines, 'line')} built on a decision that was replaced`,
9
+ ` Replaced: ${f.replaced.title}${f.replaced.sourceUrl ? ` ${f.replaced.sourceUrl}` : ''}`,
10
+ ` Replaced by: ${f.replacedBy.title}${f.replacedBy.sourceUrl ? ` ${f.replacedBy.sourceUrl}` : ''}`,
11
+ f.confirmation === 'ai-suggested'
12
+ ? ` Link: AI-suggested, not confirmed (confidence ${Number(f.confidence ?? 0).toFixed(2)})`
13
+ : ' Link: confirmed by a person',
14
+ ];
15
+ for (const h of f.files)
16
+ out.push(` ${h.file} ${plural(h.lines, 'line')} (${h.lines === 1 ? "line" : "lines"} ${shortRanges(h.ranges)})`);
17
+ return out;
18
+ }
19
+ export function reportNotes(r, extra, includeAi) {
20
+ const notes = [...extra];
21
+ if (!includeAi && r.aiExcluded > 0) {
22
+ notes.push(`${plural(r.aiExcluded, 'AI-suggested supersedes link')} left out because no person confirmed ${r.aiExcluded === 1 ? 'it' : 'them'}. Add --include-ai to include ${r.aiExcluded === 1 ? 'it' : 'them'}.`);
23
+ }
24
+ if (r.skipped.length)
25
+ notes.push(`Skipped ${plural(r.skipped.length, 'replaced decision')}: ${[...new Set(r.skipped.map((s) => s.reason))].join('; ')}.`);
26
+ if (r.filesCapped)
27
+ notes.push(`${plural(r.filesCapped, 'replaced decision')} touched more files than the per-decision cap, so only the first files were checked.`);
28
+ if (r.budgetExceeded)
29
+ notes.push('The time budget ran out before every decision was checked.');
30
+ return notes;
31
+ }
32
+ export function renderReport(r, extra, includeAi) {
33
+ const lines = [];
34
+ if (!r.findings.length) {
35
+ lines.push(`No code on ${r.ref} is still built on a replaced decision (checked ${plural(r.linksConsidered, 'supersedes link')}).`);
36
+ }
37
+ else {
38
+ const total = r.findings.reduce((n, f) => n + f.totalLines, 0);
39
+ lines.push(chalk.bold(`${plural(total, 'line')} still built on ${plural(r.findings.length, 'replaced decision')} (blame on ${r.ref})`), '');
40
+ for (const f of r.findings)
41
+ lines.push(...renderFinding(f), '');
42
+ }
43
+ for (const n of reportNotes(r, extra, includeAi))
44
+ lines.push(chalk.dim(n));
45
+ return lines.join('\n');
46
+ }
47
+ //# sourceMappingURL=stale-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-render.js","sourceRoot":"","sources":["../../src/lib/stale-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { EnvironmentConfig } from './config.js';
2
+ import { type StaleOptions, type StaleReport } from './stale-code.js';
3
+ import { type LinkSourceClient } from './stale-links.js';
4
+ export interface StaleResult {
5
+ ref: string;
6
+ findings: StaleReport['findings'];
7
+ skipped: StaleReport['skipped'];
8
+ notes: string[];
9
+ counts: {
10
+ linksConsidered: number;
11
+ aiExcluded: number;
12
+ filesCapped: number;
13
+ budgetExceeded: boolean;
14
+ };
15
+ }
16
+ /** Load the supersedes links for this environment, then look for code still built on the replaced side. Shared by the command and the MCP tool. */
17
+ export declare function runStale(env: EnvironmentConfig, client: LinkSourceClient | null, opts: Pick<StaleOptions, 'cwd' | 'includeAi' | 'maxFilesPerDecision' | 'budgetMs'>): Promise<{
18
+ report: StaleReport;
19
+ loadNotes: string[];
20
+ result: StaleResult;
21
+ }>;
22
+ //# sourceMappingURL=stale-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-run.d.ts","sourceRoot":"","sources":["../../src/lib/stale-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAiB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,KAAK,gBAAgB,EAAkB,MAAM,kBAAkB,CAAC;AAGzE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;CACvG;AAED,mJAAmJ;AACnJ,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAC/B,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,KAAK,GAAG,WAAW,GAAG,qBAAqB,GAAG,UAAU,CAAC,GACjF,OAAO,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,CAW5E"}
@@ -0,0 +1,17 @@
1
+ import { findStaleCode } from './stale-code.js';
2
+ import { loadStaleLinks } from './stale-links.js';
3
+ import { reportNotes } from './stale-render.js';
4
+ /** Load the supersedes links for this environment, then look for code still built on the replaced side. Shared by the command and the MCP tool. */
5
+ export async function runStale(env, client, opts) {
6
+ const loaded = await loadStaleLinks(env, client);
7
+ const report = await findStaleCode(loaded.links, opts);
8
+ const result = {
9
+ ref: report.ref,
10
+ findings: report.findings,
11
+ skipped: report.skipped,
12
+ notes: reportNotes(report, loaded.notes, opts.includeAi === true),
13
+ counts: { linksConsidered: report.linksConsidered, aiExcluded: report.aiExcluded, filesCapped: report.filesCapped, budgetExceeded: report.budgetExceeded },
14
+ };
15
+ return { report, loadNotes: loaded.notes, result };
16
+ }
17
+ //# sourceMappingURL=stale-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-run.js","sourceRoot":"","sources":["../../src/lib/stale-run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAuC,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAyB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUhD,mJAAmJ;AACnJ,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAsB,EACtB,MAA+B,EAC/B,IAAkF;IAElF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,MAAM,GAAgB;QAC1B,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;QACjE,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE;KAC3J,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aligndottech/cli",
3
- "version": "0.65.2",
3
+ "version": "0.66.1",
4
4
  "mcpName": "io.github.aligndottech/cli",
5
5
  "type": "module",
6
6
  "description": "Align CLI - capture decisions, check alignment, and manage connectors",