sf-agentpmd 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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +26 -0
  3. package/README.md +204 -0
  4. package/bin/dev.js +5 -0
  5. package/bin/run.js +3 -0
  6. package/dist/analyzer/action-references.d.ts +21 -0
  7. package/dist/analyzer/action-references.js +130 -0
  8. package/dist/analyzer/action-references.js.map +1 -0
  9. package/dist/analyzer/analyze.d.ts +43 -0
  10. package/dist/analyzer/analyze.js +222 -0
  11. package/dist/analyzer/analyze.js.map +1 -0
  12. package/dist/analyzer/apex-analyze.d.ts +14 -0
  13. package/dist/analyzer/apex-analyze.js +60 -0
  14. package/dist/analyzer/apex-analyze.js.map +1 -0
  15. package/dist/analyzer/apex-complexity.d.ts +27 -0
  16. package/dist/analyzer/apex-complexity.js +133 -0
  17. package/dist/analyzer/apex-complexity.js.map +1 -0
  18. package/dist/analyzer/apex-parse.d.ts +39 -0
  19. package/dist/analyzer/apex-parse.js +32 -0
  20. package/dist/analyzer/apex-parse.js.map +1 -0
  21. package/dist/analyzer/apex-resolve.d.ts +32 -0
  22. package/dist/analyzer/apex-resolve.js +59 -0
  23. package/dist/analyzer/apex-resolve.js.map +1 -0
  24. package/dist/analyzer/complexity.d.ts +30 -0
  25. package/dist/analyzer/complexity.js +126 -0
  26. package/dist/analyzer/complexity.js.map +1 -0
  27. package/dist/analyzer/parse.d.ts +51 -0
  28. package/dist/analyzer/parse.js +143 -0
  29. package/dist/analyzer/parse.js.map +1 -0
  30. package/dist/analyzer/project.d.ts +12 -0
  31. package/dist/analyzer/project.js +51 -0
  32. package/dist/analyzer/project.js.map +1 -0
  33. package/dist/analyzer/types.d.ts +76 -0
  34. package/dist/analyzer/types.js +2 -0
  35. package/dist/analyzer/types.js.map +1 -0
  36. package/dist/commands/agentpmd/analyze.d.ts +20 -0
  37. package/dist/commands/agentpmd/analyze.js +122 -0
  38. package/dist/commands/agentpmd/analyze.js.map +1 -0
  39. package/dist/commands/agentpmd/install-skill.d.ts +11 -0
  40. package/dist/commands/agentpmd/install-skill.js +33 -0
  41. package/dist/commands/agentpmd/install-skill.js.map +1 -0
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.js +3 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/renderers/csv.d.ts +6 -0
  46. package/dist/renderers/csv.js +78 -0
  47. package/dist/renderers/csv.js.map +1 -0
  48. package/dist/renderers/index.d.ts +8 -0
  49. package/dist/renderers/index.js +25 -0
  50. package/dist/renderers/index.js.map +1 -0
  51. package/dist/renderers/markdown.d.ts +12 -0
  52. package/dist/renderers/markdown.js +233 -0
  53. package/dist/renderers/markdown.js.map +1 -0
  54. package/dist/renderers/options.d.ts +20 -0
  55. package/dist/renderers/options.js +2 -0
  56. package/dist/renderers/options.js.map +1 -0
  57. package/dist/renderers/sarif.d.ts +3 -0
  58. package/dist/renderers/sarif.js +131 -0
  59. package/dist/renderers/sarif.js.map +1 -0
  60. package/dist/renderers/text.d.ts +3 -0
  61. package/dist/renderers/text.js +243 -0
  62. package/dist/renderers/text.js.map +1 -0
  63. package/oclif.manifest.json +168 -0
  64. package/package.json +97 -0
  65. package/skill/SKILL.md +103 -0
  66. package/skill/references/command-structure.md +89 -0
  67. package/skill/references/install.md +112 -0
  68. package/skill/references/output-formats.md +205 -0
  69. package/skill/references/upgrade.md +112 -0
  70. package/vendor/agentscript-parser-javascript/dist/cst-node.d.ts +83 -0
  71. package/vendor/agentscript-parser-javascript/dist/cst-node.js +238 -0
  72. package/vendor/agentscript-parser-javascript/dist/errors.d.ts +34 -0
  73. package/vendor/agentscript-parser-javascript/dist/errors.js +74 -0
  74. package/vendor/agentscript-parser-javascript/dist/expressions.d.ts +36 -0
  75. package/vendor/agentscript-parser-javascript/dist/expressions.js +682 -0
  76. package/vendor/agentscript-parser-javascript/dist/highlighter.d.ts +24 -0
  77. package/vendor/agentscript-parser-javascript/dist/highlighter.js +260 -0
  78. package/vendor/agentscript-parser-javascript/dist/index.d.ts +29 -0
  79. package/vendor/agentscript-parser-javascript/dist/index.js +35 -0
  80. package/vendor/agentscript-parser-javascript/dist/lexer.d.ts +60 -0
  81. package/vendor/agentscript-parser-javascript/dist/lexer.js +630 -0
  82. package/vendor/agentscript-parser-javascript/dist/parse-mapping.d.ts +46 -0
  83. package/vendor/agentscript-parser-javascript/dist/parse-mapping.js +549 -0
  84. package/vendor/agentscript-parser-javascript/dist/parse-sequence.d.ts +10 -0
  85. package/vendor/agentscript-parser-javascript/dist/parse-sequence.js +118 -0
  86. package/vendor/agentscript-parser-javascript/dist/parse-statements.d.ts +15 -0
  87. package/vendor/agentscript-parser-javascript/dist/parse-statements.js +519 -0
  88. package/vendor/agentscript-parser-javascript/dist/parse-templates.d.ts +15 -0
  89. package/vendor/agentscript-parser-javascript/dist/parse-templates.js +323 -0
  90. package/vendor/agentscript-parser-javascript/dist/parser.d.ts +65 -0
  91. package/vendor/agentscript-parser-javascript/dist/parser.js +163 -0
  92. package/vendor/agentscript-parser-javascript/dist/recovery.d.ts +51 -0
  93. package/vendor/agentscript-parser-javascript/dist/recovery.js +199 -0
  94. package/vendor/agentscript-parser-javascript/dist/token.d.ts +58 -0
  95. package/vendor/agentscript-parser-javascript/dist/token.js +62 -0
  96. package/vendor/agentscript-parser-javascript/package.json +19 -0
  97. package/vendor/agentscript-types/dist/comment.d.ts +11 -0
  98. package/vendor/agentscript-types/dist/comment.js +10 -0
  99. package/vendor/agentscript-types/dist/cst.d.ts +7 -0
  100. package/vendor/agentscript-types/dist/cst.js +8 -0
  101. package/vendor/agentscript-types/dist/diagnostic.d.ts +34 -0
  102. package/vendor/agentscript-types/dist/diagnostic.js +23 -0
  103. package/vendor/agentscript-types/dist/index.d.ts +9 -0
  104. package/vendor/agentscript-types/dist/index.js +10 -0
  105. package/vendor/agentscript-types/dist/position.d.ts +11 -0
  106. package/vendor/agentscript-types/dist/position.js +16 -0
  107. package/vendor/agentscript-types/dist/syntax-node.d.ts +39 -0
  108. package/vendor/agentscript-types/dist/syntax-node.js +8 -0
  109. package/vendor/agentscript-types/package.json +15 -0
@@ -0,0 +1,222 @@
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ import { basename, dirname, join, relative, resolve } from 'node:path';
3
+ import { collectDeclarations, collectReferences } from './action-references.js';
4
+ import { analyzeReferencedApex } from './apex-analyze.js';
5
+ import { collectScopes, complexityForFile } from './complexity.js';
6
+ import { extractDeveloperName, parseAgentSource } from './parse.js';
7
+ /**
8
+ * Error thrown when --api-name filters produce no matches. Carries the
9
+ * candidate list so callers can show a useful hint.
10
+ */
11
+ export class NoMatchingBundlesError extends Error {
12
+ requested;
13
+ available;
14
+ constructor(requested, available) {
15
+ super(`No agent bundle matched ${requested.map(n => `'${n}'`).join(', ')}. ` +
16
+ `Available: ${available.map((b) => formatBundleIdentity(b)).join(', ')}`);
17
+ this.requested = requested;
18
+ this.available = available;
19
+ this.name = 'NoMatchingBundlesError';
20
+ }
21
+ }
22
+ function formatBundleIdentity(b) {
23
+ if (b.developerName && b.developerName !== b.dirName) {
24
+ return `${b.developerName} (dir: ${b.dirName})`;
25
+ }
26
+ return b.dirName;
27
+ }
28
+ /**
29
+ * Analyze AgentScript bundles under one or more source roots.
30
+ *
31
+ * Backward-compatible: pass a single path (string) for the legacy
32
+ * single-root case. Pass an array of paths for multi-root analysis
33
+ * (e.g. multiple `packageDirectories` in an sfdx project).
34
+ */
35
+ export async function analyzeSource(rootPathOrPaths, options = {}) {
36
+ const rawRoots = Array.isArray(rootPathOrPaths)
37
+ ? rootPathOrPaths
38
+ : [rootPathOrPaths];
39
+ if (rawRoots.length === 0) {
40
+ throw new Error('analyzeSource requires at least one source path');
41
+ }
42
+ const absRoots = rawRoots.map(p => resolve(p));
43
+ const reportBase = options.reportBase
44
+ ? resolve(options.reportBase)
45
+ : absRoots.length === 1
46
+ ? absRoots[0]
47
+ : longestCommonAncestor(absRoots);
48
+ const allFiles = [];
49
+ for (const root of absRoots) {
50
+ const files = await findAgentFiles(root);
51
+ for (const f of files)
52
+ if (!allFiles.includes(f))
53
+ allFiles.push(f);
54
+ }
55
+ allFiles.sort();
56
+ const filteredFiles = await filterByApiNames(allFiles, options.apiNames);
57
+ const fileReports = [];
58
+ for (const file of filteredFiles) {
59
+ fileReports.push(await analyzeFile(file, reportBase));
60
+ }
61
+ const apex = await analyzeReferencedApex({
62
+ agentAbsPaths: filteredFiles,
63
+ apexSourceOverride: options.apexSourceOverride,
64
+ fileReports,
65
+ sourceDirRoot: reportBase,
66
+ });
67
+ const report = {
68
+ apexClasses: apex.classes,
69
+ byTargetKind: tallyTargets(fileReports),
70
+ files: fileReports,
71
+ totalApexComplexity: apex.classes.reduce((acc, c) => acc + c.classComplexity, 0),
72
+ totalComplexity: fileReports.reduce((acc, f) => acc + f.fileComplexity, 0),
73
+ totalDeclarations: fileReports.reduce((acc, f) => acc + f.declarations.length, 0),
74
+ totalReferences: fileReports.reduce((acc, f) => acc + f.references.length, 0),
75
+ unresolvedApexTargets: apex.unresolved,
76
+ };
77
+ return report;
78
+ }
79
+ export async function analyzeFile(absPath, base) {
80
+ const source = await readFile(absPath, 'utf8');
81
+ const root = parseAgentSource(source);
82
+ const cc = complexityForFile(root);
83
+ const { procedures } = cc;
84
+ const scopes = collectScopes(root);
85
+ const declarations = [];
86
+ const references = [];
87
+ for (const s of scopes) {
88
+ declarations.push(...collectDeclarations(s));
89
+ references.push(...collectReferences(s));
90
+ }
91
+ return {
92
+ declarations,
93
+ fileComplexity: cc.total,
94
+ parseErrors: [], // CST is error-tolerant; surface diagnostics in a later iteration.
95
+ path: relative(base, absPath),
96
+ procedures,
97
+ references,
98
+ };
99
+ }
100
+ async function findAgentFiles(root) {
101
+ const s = await stat(root).catch(() => { });
102
+ if (!s)
103
+ throw new Error(`source path does not exist: ${root}`);
104
+ if (s.isFile())
105
+ return root.endsWith('.agent') ? [root] : [];
106
+ const out = [];
107
+ const visit = async (dir) => {
108
+ const entries = await readdir(dir, { withFileTypes: true });
109
+ for (const e of entries) {
110
+ if (e.name.startsWith('.'))
111
+ continue;
112
+ if (e.name === 'node_modules' || e.name === 'vendor' || e.name === 'lib')
113
+ continue;
114
+ const full = join(dir, e.name);
115
+ if (e.isDirectory())
116
+ await visit(full);
117
+ else if (e.isFile() && e.name.endsWith('.agent'))
118
+ out.push(full);
119
+ }
120
+ };
121
+ await visit(root);
122
+ out.sort();
123
+ return out;
124
+ }
125
+ function tallyTargets(files) {
126
+ const acc = {
127
+ apex: 0,
128
+ flow: 0,
129
+ prompt: 0,
130
+ unknown: 0,
131
+ utils: 0,
132
+ };
133
+ for (const f of files)
134
+ for (const d of f.declarations)
135
+ acc[d.targetKind]++;
136
+ return acc;
137
+ }
138
+ /**
139
+ * Filter the discovered .agent file list by the supplied `--api-name`
140
+ * values. A bundle matches if (a) its directory name equals a requested
141
+ * name, or (b) its `config.developer_name` equals one. Bundles whose dir
142
+ * name already matches are kept without parsing; only the remainder are
143
+ * parsed for `developer_name` resolution.
144
+ *
145
+ * Throws NoMatchingBundlesError when filters were supplied but nothing
146
+ * matched, so callers can show the available list.
147
+ */
148
+ async function filterByApiNames(files, apiNames) {
149
+ if (!apiNames || apiNames.length === 0)
150
+ return files;
151
+ const wanted = new Set(apiNames);
152
+ const matches = [];
153
+ const unmatched = [];
154
+ for (const file of files) {
155
+ const dirName = basename(dirname(file));
156
+ if (wanted.has(dirName)) {
157
+ matches.push(file);
158
+ }
159
+ else {
160
+ unmatched.push(file);
161
+ }
162
+ }
163
+ if (matches.length === files.length || unmatched.length === 0) {
164
+ return matches;
165
+ }
166
+ // Slow path: parse the still-unmatched files to read developer_name.
167
+ const identities = [];
168
+ for (const file of unmatched) {
169
+ const dirName = basename(dirname(file));
170
+ const developerName = await readDeveloperName(file);
171
+ identities.push({ developerName, dirName, path: file });
172
+ if (developerName && wanted.has(developerName)) {
173
+ matches.push(file);
174
+ }
175
+ }
176
+ if (matches.length === 0) {
177
+ // Build the full candidate list (matched dirs + parsed unmatched) for
178
+ // the error message.
179
+ const all = [];
180
+ for (const file of files) {
181
+ const existing = identities.find(i => i.path === file);
182
+ if (existing) {
183
+ all.push(existing);
184
+ }
185
+ else {
186
+ all.push({
187
+ developerName: await readDeveloperName(file),
188
+ dirName: basename(dirname(file)),
189
+ path: file,
190
+ });
191
+ }
192
+ }
193
+ all.sort((a, b) => a.dirName.localeCompare(b.dirName));
194
+ throw new NoMatchingBundlesError(apiNames, all);
195
+ }
196
+ matches.sort();
197
+ return matches;
198
+ }
199
+ async function readDeveloperName(file) {
200
+ const source = await readFile(file, 'utf8');
201
+ const root = parseAgentSource(source);
202
+ return extractDeveloperName(root);
203
+ }
204
+ function longestCommonAncestor(paths) {
205
+ if (paths.length === 0)
206
+ return process.cwd();
207
+ if (paths.length === 1)
208
+ return paths[0];
209
+ const split = paths.map(p => p.split('/'));
210
+ const minLen = Math.min(...split.map(s => s.length));
211
+ const common = [];
212
+ for (let i = 0; i < minLen; i++) {
213
+ const seg = split[0][i];
214
+ if (split.every(s => s[i] === seg))
215
+ common.push(seg);
216
+ else
217
+ break;
218
+ }
219
+ const joined = common.join('/') || '/';
220
+ return joined;
221
+ }
222
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/analyzer/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ApexClassReport, FileReport } from './types.js';
2
+ /** Inputs needed to resolve and analyze every apex:// target in the bundle. */
3
+ export interface ApexAnalyzeInputs {
4
+ /** Absolute paths of every .agent file analyzed, parallel to fileReports. */
5
+ agentAbsPaths: string[];
6
+ apexSourceOverride?: string;
7
+ fileReports: FileReport[];
8
+ sourceDirRoot: string;
9
+ }
10
+ export interface ApexAnalyzeOutputs {
11
+ classes: ApexClassReport[];
12
+ unresolved: string[];
13
+ }
14
+ export declare function analyzeReferencedApex(inputs: ApexAnalyzeInputs): Promise<ApexAnalyzeOutputs>;
@@ -0,0 +1,60 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { basename, relative } from 'node:path';
3
+ import { complexityOfMethod, methodsInCompilationUnit } from './apex-complexity.js';
4
+ import { parseApexSource } from './apex-parse.js';
5
+ import { extractApexClassName, resolveApexClassPath, } from './apex-resolve.js';
6
+ export async function analyzeReferencedApex(inputs) {
7
+ const byPath = new Map();
8
+ const unresolved = new Set();
9
+ for (let i = 0; i < inputs.fileReports.length; i++) {
10
+ const fr = inputs.fileReports[i];
11
+ const agentAbs = inputs.agentAbsPaths[i];
12
+ for (const decl of fr.declarations) {
13
+ if (decl.targetKind !== 'apex' || !decl.target)
14
+ continue;
15
+ const className = extractApexClassName(decl.target);
16
+ if (!className) {
17
+ unresolved.add(decl.target);
18
+ continue;
19
+ }
20
+ const path = await resolveApexClassPath(className, {
21
+ agentFilePath: agentAbs,
22
+ apexSourceOverride: inputs.apexSourceOverride,
23
+ sourceDirRoot: inputs.sourceDirRoot,
24
+ });
25
+ if (!path) {
26
+ unresolved.add(decl.target);
27
+ continue;
28
+ }
29
+ const existing = byPath.get(path);
30
+ if (existing) {
31
+ if (!existing.referencedBy.includes(fr.path)) {
32
+ existing.referencedBy.push(fr.path);
33
+ }
34
+ continue;
35
+ }
36
+ const report = await analyzeApexClass(path, className, [fr.path], inputs.sourceDirRoot);
37
+ byPath.set(path, report);
38
+ }
39
+ }
40
+ return {
41
+ classes: [...byPath.values()].sort((a, b) => a.className.localeCompare(b.className)),
42
+ unresolved: [...unresolved].sort(),
43
+ };
44
+ }
45
+ async function analyzeApexClass(absPath, className, referencedBy, sourceDirRoot) {
46
+ const source = await readFile(absPath, 'utf8');
47
+ const cu = parseApexSource(source);
48
+ const methodCtxs = methodsInCompilationUnit(cu);
49
+ const methods = methodCtxs.map((m) => complexityOfMethod(m));
50
+ const classComplexity = methods.reduce((acc, m) => acc + m.complexity, 0);
51
+ return {
52
+ classComplexity,
53
+ className: className || basename(absPath, '.cls'),
54
+ methods,
55
+ parseErrors: [],
56
+ path: relative(sourceDirRoot, absPath),
57
+ referencedBy,
58
+ };
59
+ }
60
+ //# sourceMappingURL=apex-analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apex-analyze.js","sourceRoot":"","sources":["../../src/analyzer/apex-analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type CompilationUnitContext, ConstructorDeclarationContext, MethodDeclarationContext } from '@apexdevtools/apex-parser';
2
+ import type { ApexMethodCC } from './types.js';
3
+ /**
4
+ * Standard McCabe Cyclomatic Complexity for Apex, per the SonarQube /
5
+ * PMD CyclomaticComplexity convention used in the categorization
6
+ * whitepaper § 7:
7
+ *
8
+ * CC = 1
9
+ * + count(if)
10
+ * + count(for) // includes enhanced-for (for-each)
11
+ * + count(while)
12
+ * + count(do-while)
13
+ * + count(when arm) // each switch `when X { ... }`; `when else` excluded
14
+ * + count(catch) // each catch clause
15
+ * + count(ternary ?:)
16
+ * + count(&&)
17
+ * + count(||)
18
+ *
19
+ * Not counted: else, when else, finally, try itself.
20
+ */
21
+ export declare function complexityOfMethod(methodCtx: ConstructorDeclarationContext | MethodDeclarationContext): ApexMethodCC;
22
+ /**
23
+ * Enumerate every method and constructor in a compilation unit, including
24
+ * those nested inside inner classes. Each gets its own CC entry per the
25
+ * v2 scope ("all methods in any class touched by apex://").
26
+ */
27
+ export declare function methodsInCompilationUnit(cu: CompilationUnitContext): Array<ConstructorDeclarationContext | MethodDeclarationContext>;
@@ -0,0 +1,133 @@
1
+ import { CatchClauseContext, CondExpressionContext, ConstructorDeclarationContext, DoWhileStatementContext, ForStatementContext, IfStatementContext, LogAndExpressionContext, LogOrExpressionContext, MethodDeclarationContext, WhenControlContext, WhileStatementContext, } from '@apexdevtools/apex-parser';
2
+ import { locOfCtx, walkParseTree } from './apex-parse.js';
3
+ /**
4
+ * Standard McCabe Cyclomatic Complexity for Apex, per the SonarQube /
5
+ * PMD CyclomaticComplexity convention used in the categorization
6
+ * whitepaper § 7:
7
+ *
8
+ * CC = 1
9
+ * + count(if)
10
+ * + count(for) // includes enhanced-for (for-each)
11
+ * + count(while)
12
+ * + count(do-while)
13
+ * + count(when arm) // each switch `when X { ... }`; `when else` excluded
14
+ * + count(catch) // each catch clause
15
+ * + count(ternary ?:)
16
+ * + count(&&)
17
+ * + count(||)
18
+ *
19
+ * Not counted: else, when else, finally, try itself.
20
+ */
21
+ export function complexityOfMethod(methodCtx) {
22
+ const body = methodCtx.block();
23
+ const contributors = [];
24
+ if (body) {
25
+ walkParseTree(body, node => {
26
+ // Don't descend into nested methods/constructors — they have their
27
+ // own CC counted separately.
28
+ if (node !== methodCtx &&
29
+ (node instanceof MethodDeclarationContext ||
30
+ node instanceof ConstructorDeclarationContext)) {
31
+ // walkParseTree will still recurse into this node's subtree.
32
+ // We can't easily prune, so we filter by parentage: skip
33
+ // contributors whose nearest enclosing method/ctor is not us.
34
+ return;
35
+ }
36
+ const kind = classifyContributor(node);
37
+ if (kind !== undefined && enclosingMethod(node) === methodCtx) {
38
+ contributors.push({ kind, location: locOfCtx(node) });
39
+ }
40
+ });
41
+ }
42
+ const name = methodCtx instanceof ConstructorDeclarationContext
43
+ ? methodCtx.qualifiedName()?.getText() ?? '<ctor>'
44
+ : methodCtx.id()?.getText() ?? '<anon>';
45
+ const signature = renderSignature(methodCtx, name);
46
+ return {
47
+ complexity: 1 + contributors.length,
48
+ contributors,
49
+ kind: methodCtx instanceof ConstructorDeclarationContext ? 'constructor' : 'method',
50
+ location: locOfCtx(methodCtx),
51
+ name,
52
+ signature,
53
+ };
54
+ }
55
+ function classifyContributor(node) {
56
+ if (node instanceof IfStatementContext)
57
+ return 'if_statement';
58
+ if (node instanceof ForStatementContext)
59
+ return 'for_statement';
60
+ if (node instanceof WhileStatementContext)
61
+ return 'while_statement';
62
+ if (node instanceof DoWhileStatementContext)
63
+ return 'do_while_statement';
64
+ if (node instanceof WhenControlContext) {
65
+ // `when else { ... }` is the default arm → don't count it.
66
+ const wv = node.whenValue();
67
+ const elseTok = wv && typeof wv.ELSE === 'function' ? wv.ELSE() : undefined;
68
+ return elseTok ? undefined : 'when_arm';
69
+ }
70
+ if (node instanceof CatchClauseContext)
71
+ return 'catch_clause';
72
+ if (node instanceof CondExpressionContext)
73
+ return 'ternary';
74
+ if (node instanceof LogAndExpressionContext)
75
+ return 'short_circuit_and';
76
+ if (node instanceof LogOrExpressionContext)
77
+ return 'short_circuit_or';
78
+ return undefined;
79
+ }
80
+ function enclosingMethod(node) {
81
+ let p = node.parentCtx;
82
+ while (p) {
83
+ if (p instanceof MethodDeclarationContext || p instanceof ConstructorDeclarationContext) {
84
+ return p;
85
+ }
86
+ p = p.parentCtx;
87
+ }
88
+ return undefined;
89
+ }
90
+ function renderSignature(ctx, name) {
91
+ const paramsText = renderFormalParameters(ctx.formalParameters());
92
+ if (ctx instanceof MethodDeclarationContext) {
93
+ const retType = ctx.typeRef()?.getText() ?? (ctx.VOID() ? 'void' : '');
94
+ return `${retType ? retType + ' ' : ''}${name}${paramsText}`;
95
+ }
96
+ return `${name}${paramsText}`;
97
+ }
98
+ /**
99
+ * ANTLR's `getText()` concatenates child tokens with no whitespace, which
100
+ * mangles parameter lists like `List<Request> requests` into
101
+ * `List<Request>requests`. Walk the structured children and rebuild a
102
+ * properly-spaced signature.
103
+ */
104
+ function renderFormalParameters(params) {
105
+ if (!params)
106
+ return '()';
107
+ const list = params.formalParameterList();
108
+ if (!list)
109
+ return '()';
110
+ const parts = list.formalParameter_list().map(p => {
111
+ const type = p.typeRef()?.getText() ?? '';
112
+ const id = p.id()?.getText() ?? '';
113
+ return type && id ? `${type} ${id}` : type || id;
114
+ });
115
+ return `(${parts.join(', ')})`;
116
+ }
117
+ /**
118
+ * Enumerate every method and constructor in a compilation unit, including
119
+ * those nested inside inner classes. Each gets its own CC entry per the
120
+ * v2 scope ("all methods in any class touched by apex://").
121
+ */
122
+ export function methodsInCompilationUnit(cu) {
123
+ const out = [];
124
+ walkParseTree(cu, node => {
125
+ if ((node instanceof MethodDeclarationContext ||
126
+ node instanceof ConstructorDeclarationContext) &&
127
+ node.block()) {
128
+ out.push(node);
129
+ }
130
+ });
131
+ return out;
132
+ }
133
+ //# sourceMappingURL=apex-complexity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apex-complexity.js","sourceRoot":"","sources":["../../src/analyzer/apex-complexity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { CompilationUnitContext } from '@apexdevtools/apex-parser';
2
+ /**
3
+ * Structural view of an ANTLR parse-tree node covering only the members this
4
+ * analyzer touches. `@apexdevtools/apex-parser` 5.x ships generated context
5
+ * types that extend antlr4's `ParserRuleContext`, but antlr4 4.13.x does not
6
+ * surface `ParserRuleContext` (nor its inherited `getText`/`getChild`) through
7
+ * its NodeNext `.d.cts` re-export chain, so importing it directly fails to
8
+ * resolve. We model the contract locally instead — the runtime objects carry
9
+ * all of these methods.
10
+ */
11
+ export interface ApexRuleNode {
12
+ getChild(i: number): unknown;
13
+ getChildCount(): number;
14
+ getText(): string;
15
+ parentCtx?: ApexRuleNode | undefined;
16
+ start?: {
17
+ column?: number;
18
+ line?: number;
19
+ text?: string;
20
+ };
21
+ stop?: {
22
+ column?: number;
23
+ line?: number;
24
+ text?: string;
25
+ };
26
+ }
27
+ export declare function parseApexSource(source: string): CompilationUnitContext;
28
+ export declare function locOfCtx(ctx: ApexRuleNode): {
29
+ endCol: number;
30
+ endRow: number;
31
+ startCol: number;
32
+ startRow: number;
33
+ };
34
+ /**
35
+ * Walk an ANTLR parse-tree node depth-first, invoking the visitor on every
36
+ * descendant (including the start node). Terminal/error nodes have no
37
+ * children and are visited like any other node — caller filters by type.
38
+ */
39
+ export declare function walkParseTree(node: ApexRuleNode, visit: (n: ApexRuleNode) => void): void;
@@ -0,0 +1,32 @@
1
+ import { ApexParserFactory, } from '@apexdevtools/apex-parser';
2
+ export function parseApexSource(source) {
3
+ const parser = ApexParserFactory.createParser(source);
4
+ return parser.compilationUnit();
5
+ }
6
+ export function locOfCtx(ctx) {
7
+ const { start } = ctx;
8
+ const stop = ctx.stop ?? ctx.start;
9
+ return {
10
+ endCol: (stop?.column ?? 0) + (stop?.text?.length ?? 0),
11
+ endRow: stop?.line ?? 0,
12
+ startCol: start?.column ?? 0,
13
+ startRow: start?.line ?? 0,
14
+ };
15
+ }
16
+ /**
17
+ * Walk an ANTLR parse-tree node depth-first, invoking the visitor on every
18
+ * descendant (including the start node). Terminal/error nodes have no
19
+ * children and are visited like any other node — caller filters by type.
20
+ */
21
+ export function walkParseTree(node, visit) {
22
+ visit(node);
23
+ const childCount = typeof node.getChildCount === 'function' ? node.getChildCount() : 0;
24
+ for (let i = 0; i < childCount; i++) {
25
+ const c = node.getChild(i);
26
+ // Filter to ParserRuleContext children only — terminals don't carry rules.
27
+ if (c && typeof c.getChildCount === 'function') {
28
+ walkParseTree(c, visit);
29
+ }
30
+ }
31
+ }
32
+ //# sourceMappingURL=apex-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apex-parse.js","sourceRoot":"","sources":["../../src/analyzer/apex-parse.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,2BAA2B,CAAC;AAoBnC,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,MAAM,GAAG,iBAAiB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,eAAe,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAiB;IACxC,MAAM,EAAC,KAAK,EAAC,GAAG,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC;IACnC,OAAO;QACL,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;QACvD,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;QACvB,QAAQ,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;QAC5B,QAAQ,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;KAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAkB,EAClB,KAAgC;IAEhC,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,MAAM,UAAU,GACd,OAAO,IAAI,CAAC,aAAa,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC3B,2EAA2E;QAC3E,IAAI,CAAC,IAAI,OAAQ,CAAkB,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;YACjE,aAAa,CAAC,CAAiB,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,32 @@
1
+ export declare function extractApexClassName(uri: string): string | undefined;
2
+ export interface ResolveOptions {
3
+ /**
4
+ * The .agent file that holds the apex:// reference. Used as the seed for
5
+ * the upward walk.
6
+ */
7
+ agentFilePath: string;
8
+ /**
9
+ * Optional `--apex-source` override. When set, classes are looked up here
10
+ * before falling back to the upward walk.
11
+ */
12
+ apexSourceOverride?: string;
13
+ /**
14
+ * The CLI `--source-dir` value. We never walk above this directory.
15
+ */
16
+ sourceDirRoot: string;
17
+ }
18
+ /**
19
+ * Find `classes/<ClassName>.cls` by walking up from the .agent file. The walk
20
+ * stops at the CLI's source-dir root so we never escape it. Returns the
21
+ * absolute path or undefined.
22
+ *
23
+ * Path conventions covered:
24
+ * - sfdx default: <pkg>/main/default/classes/<X>.cls, with the .agent in
25
+ * <pkg>/main/default/aiAuthoringBundles/<Bundle>/<X>.agent
26
+ * - flat fixtures: <root>/classes/<X>.cls, with .agent under
27
+ * <root>/aiAuthoringBundles/<Bundle>/<X>.agent
28
+ * - explicit override: --apex-source <dir>/<X>.cls
29
+ */
30
+ export declare function resolveApexClassPath(className: string, opts: ResolveOptions): Promise<string | undefined>;
31
+ /** True if `child` is the same as or a subpath of `parent` (path-segment safe). */
32
+ export declare function isUnder(child: string, parent: string): boolean;
@@ -0,0 +1,59 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
3
+ const APEX_SCHEME = /^apex:\/\/(.+)$/;
4
+ export function extractApexClassName(uri) {
5
+ const m = APEX_SCHEME.exec(uri);
6
+ if (!m)
7
+ return undefined;
8
+ // `apex://Foo` or `apex://namespace__Foo`; take the trailing simple name.
9
+ const raw = m[1].trim();
10
+ const dot = raw.lastIndexOf('.');
11
+ return dot === -1 ? raw : raw.slice(dot + 1);
12
+ }
13
+ /**
14
+ * Find `classes/<ClassName>.cls` by walking up from the .agent file. The walk
15
+ * stops at the CLI's source-dir root so we never escape it. Returns the
16
+ * absolute path or undefined.
17
+ *
18
+ * Path conventions covered:
19
+ * - sfdx default: <pkg>/main/default/classes/<X>.cls, with the .agent in
20
+ * <pkg>/main/default/aiAuthoringBundles/<Bundle>/<X>.agent
21
+ * - flat fixtures: <root>/classes/<X>.cls, with .agent under
22
+ * <root>/aiAuthoringBundles/<Bundle>/<X>.agent
23
+ * - explicit override: --apex-source <dir>/<X>.cls
24
+ */
25
+ export async function resolveApexClassPath(className, opts) {
26
+ const candidates = [];
27
+ if (opts.apexSourceOverride) {
28
+ const override = isAbsolute(opts.apexSourceOverride)
29
+ ? opts.apexSourceOverride
30
+ : resolve(opts.apexSourceOverride);
31
+ candidates.push(join(override, `${className}.cls`), join(override, 'classes', `${className}.cls`));
32
+ }
33
+ const stop = resolve(opts.sourceDirRoot);
34
+ let dir = dirname(resolve(opts.agentFilePath));
35
+ while (true) {
36
+ candidates.push(join(dir, 'classes', `${className}.cls`));
37
+ if (dir === stop)
38
+ break;
39
+ const parent = dirname(dir);
40
+ if (parent === dir)
41
+ break;
42
+ dir = parent;
43
+ }
44
+ for (const c of candidates) {
45
+ const s = await stat(c).catch(() => { });
46
+ if (s?.isFile())
47
+ return c;
48
+ }
49
+ return undefined;
50
+ }
51
+ /** True if `child` is the same as or a subpath of `parent` (path-segment safe). */
52
+ export function isUnder(child, parent) {
53
+ const c = resolve(child);
54
+ const p = resolve(parent);
55
+ if (c === p)
56
+ return true;
57
+ return c.startsWith(p.endsWith(sep) ? p : p + sep);
58
+ }
59
+ //# sourceMappingURL=apex-resolve.js.map