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
package/skill/SKILL.md ADDED
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: agentforcepmd
3
+ description: Use this skill whenever working with the sf-agentpmd SF CLI plugin — discovering its commands, installing or upgrading it, running it against an Agentforce / AgentScript project, or interpreting its text / JSON / Markdown / SARIF / CSV outputs. Triggers on mentions of `sf agentpmd`, "AgentScript cyclomatic complexity", "Agent CC", "McCabe analysis of .agent files", "agent PMD", or any request to compute or report complexity / action-reference inventory for AgentScript bundles and the Apex backing logic they invoke.
4
+ metadata:
5
+ type: skill
6
+ version: "0.1.0"
7
+ last_updated: "2026-05-19"
8
+ ---
9
+
10
+ # sf-agentpmd
11
+
12
+ `sf-agentpmd` is an SF CLI plugin that computes **standard McCabe
13
+ cyclomatic complexity** for AgentScript (`.agent`) bundles and the Apex
14
+ classes they invoke through `apex://` action targets. It also inventories
15
+ action declarations and references, and emits a "CC by location"
16
+ roll-up (AgentScript vs. Apex vs. Combined) suitable for posture analysis
17
+ or static-analysis CI gates.
18
+
19
+ ## What it does at a glance
20
+
21
+ - Walks every `.agent` file under a source directory (or auto-discovers
22
+ `sfdx-project.json` and uses its `packageDirectories`).
23
+ - For each `before_reasoning:`, `after_reasoning:`, and
24
+ `reasoning.instructions:` block, computes McCabe CC.
25
+ - For each `actions:` declaration, classifies the `target:` URI
26
+ (`apex://`, `flow://`, `prompt://`).
27
+ - For every `apex://ClassName` target, resolves the `.cls` file and
28
+ computes per-method McCabe CC with the same convention SonarQube / PMD
29
+ use for Apex.
30
+ - Renders the result as **text** (TTY-aware), **JSON** (SF CLI envelope),
31
+ **markdown** (with a Mermaid by-location chart), **SARIF** (for GitHub
32
+ Code Scanning), or **CSV** (for spreadsheet pivots).
33
+
34
+ ## Task domains
35
+
36
+ Identify which of the four common questions the user is asking and read
37
+ the matching reference file before answering.
38
+
39
+ ### "What commands does this plugin offer?"
40
+
41
+ Read [`references/command-structure.md`](references/command-structure.md).
42
+ Covers the topic / command tree, every flag, defaults, and short aliases.
43
+
44
+ ### "How do I install it?"
45
+
46
+ Read [`references/install.md`](references/install.md). Covers both:
47
+ - **Today (pre-publication):** clone + `npm install` + `npm run build` +
48
+ `sf plugins link`.
49
+ - **Future (after npm publish):** `sf plugins install sf-agentpmd`.
50
+
51
+ ### "How do I upgrade it?"
52
+
53
+ Read [`references/upgrade.md`](references/upgrade.md). Covers the
54
+ linked-checkout refresh path (`git pull && npm run build`) and the
55
+ published-plugin path (`sf plugins update` or
56
+ `sf plugins install <name>@latest`).
57
+
58
+ ### "How do I make sense of the output?"
59
+
60
+ Read [`references/output-formats.md`](references/output-formats.md).
61
+ Covers every format the plugin emits, what each column / field means,
62
+ how to read the "CC by location" roll-up, and which format to pick for
63
+ which downstream consumer (terminal, PR comment, CI gate, spreadsheet).
64
+
65
+ ## Rules that always apply
66
+
67
+ 1. **Always include `--json`** when programmatically consuming output
68
+ (CI, follow-on `jq` pipelines, another plugin). Non-JSON formats are
69
+ for humans.
70
+ 2. **Prefer auto-discovery.** When run from inside an sfdx project,
71
+ omit `--source-dir`. The plugin will walk up to `sfdx-project.json`
72
+ and use its `packageDirectories`. Print the resolved root from the
73
+ header line to confirm.
74
+ 3. **Apex CC follow-through happens for free.** Any `apex://` target
75
+ referenced by an analyzed `.agent` file resolves to a sibling
76
+ `classes/<ClassName>.cls` and gets walked automatically — no flag
77
+ needed. Override with `--apex-source <dir>` only when the layout
78
+ differs from the standard sfdx convention.
79
+ 4. **`--fail-on N` thresholds on combined CC** (agent + Apex), not
80
+ agent-only. This is intentional for CI gates: a posture that pushes
81
+ complexity from AgentScript into Apex shouldn't slip past the gate.
82
+
83
+ ## Common workflows
84
+
85
+ | You want to… | Run |
86
+ | --- | --- |
87
+ | Inspect a whole sfdx project | `sf agentpmd analyze` (inside the project) |
88
+ | Inspect a specific bundle | `sf agentpmd analyze -n <DeveloperName>` |
89
+ | Generate a PR / whitepaper appendix | `sf agentpmd analyze --format markdown > report.md` |
90
+ | Gate a PR in CI | `sf agentpmd analyze --format sarif > out.sarif` (upload to Code Scanning) or `--fail-on 50` |
91
+ | Pipe into another sf tool | `sf agentpmd analyze --json \| jq '.result.apexClasses[].classComplexity'` |
92
+
93
+ ## When *not* to use this plugin
94
+
95
+ - **General Apex static analysis.** That's PMD / sfdx-scanner /
96
+ `@salesforce/code-analyzer`. sf-agentpmd's Apex pass only walks classes
97
+ reachable via `apex://` targets from an `.agent` file.
98
+ - **Old Bot Builder XML or GenAiPlannerBundle XML.** Currently unsupported
99
+ (see the project's `TODO.md`). The plugin will skip them silently and
100
+ may dramatically under-report the implementation surface of agents
101
+ that store actions in a sibling planner bundle.
102
+ - **Flow CC.** Not implemented; tracked in `TODO.md` § 9 / Flow
103
+ incorporation.
@@ -0,0 +1,89 @@
1
+ # Command structure
2
+
3
+ `sf-agentpmd` registers the topic `agentpmd` with one command:
4
+
5
+ ```
6
+ sf agentpmd analyze [flags]
7
+ ```
8
+
9
+ Discover the command surface yourself with `sf agentpmd --help` or
10
+ `sf agentpmd analyze --help`.
11
+
12
+ ## The one command — `sf agentpmd analyze`
13
+
14
+ Compute McCabe cyclomatic complexity and action-reference inventory for
15
+ AgentScript bundles and their Apex backing logic.
16
+
17
+ ### Flags
18
+
19
+ | Flag | Short | Type | Default | Purpose |
20
+ | --- | --- | --- | --- | --- |
21
+ | `--source-dir` | `-d` | path | _auto_ | Directory or single `.agent` file to analyze. When omitted, walks up from cwd looking for `sfdx-project.json` and uses its `packageDirectories`. |
22
+ | `--api-name` | `-n` | string (repeatable) | _all_ | Filter to specific bundles by api-name. Matches against the bundle directory name **or** `config.developer_name` inside the `.agent`. Pass multiple times to union: `-n A -n B`. |
23
+ | `--apex-source` | — | path | _auto_ | Override directory for resolving `apex://` targets. Default: walks up from each `.agent` looking for a sibling `classes/` directory. |
24
+ | `--format` | — | enum | `text` | Non-JSON output: `text`, `markdown`, `sarif`, `csv`. Ignored when `--json` is set. |
25
+ | `--width` | — | integer | `60` | Rule width for the text renderer. |
26
+ | `--ascii` | — | boolean | _auto_ | Force ASCII-only output (no emoji / Unicode box chars). Auto-enabled when stdout isn't a TTY (e.g. piped). |
27
+ | `--no-color` | — | boolean | `false` | Disable ANSI color in the text renderer. `NO_COLOR` env var also disables. |
28
+ | `--sarif-warning` | — | integer | `10` | SARIF "warning" level threshold (per-method/per-procedure CC). |
29
+ | `--sarif-error` | — | integer | `20` | SARIF "error" level threshold. |
30
+ | `--fail-on` | — | integer | _off_ | Exit non-zero (code 2) if **combined** (agent + Apex) CC ≥ N. CI gate. |
31
+ | `--json` | — | boolean | `false` | Emit SF CLI JSON envelope. Takes precedence over `--format`. |
32
+ | `--flags-dir` | — | path | — | Standard SF CLI flag — import flag values from a directory. Inherited from `@salesforce/sf-plugins-core`. |
33
+
34
+ ### Auto-discovery rules
35
+
36
+ When `--source-dir` is omitted, the plugin walks up from `process.cwd()`
37
+ looking for `sfdx-project.json`. If found, it uses the project's
38
+ `packageDirectories` as source roots and prints
39
+ `Analyzing sfdx project <root> (N package director{y|ies}).` as the
40
+ first line of human output. If no project file is found, it errors with
41
+ `No --source-dir was provided and no sfdx-project.json was found …`.
42
+
43
+ This matches the standard SF CLI ergonomics (`sf project deploy start`,
44
+ `sf project retrieve start`, etc.).
45
+
46
+ ### Filter semantics for `--api-name`
47
+
48
+ A bundle matches a `-n <X>` value if **either**:
49
+
50
+ 1. The bundle's directory name equals `<X>` (fast path, no parse).
51
+ 2. The bundle's `config.developer_name:` value equals `<X>` (slow path,
52
+ used only when (1) misses).
53
+
54
+ If multiple `-n` values are supplied, the union is included. When none
55
+ match anything discovered, the command errors with the list of
56
+ available bundle names so the user can correct.
57
+
58
+ The slow path means `--api-name` works even when the bundle's developer
59
+ name has been edited away from the directory name — common after a
60
+ manual rename.
61
+
62
+ ### Exit codes
63
+
64
+ | Code | Meaning |
65
+ | --- | --- |
66
+ | 0 | Success. Report rendered to stdout. |
67
+ | 1 | Standard usage / configuration error (missing flag, unknown bundle, no project found, invalid path). |
68
+ | 2 | `--fail-on N` was set and combined CC reached or exceeded N. |
69
+
70
+ ### What "combined CC" includes
71
+
72
+ - Sum of CC of every analyzed `.agent` procedure body
73
+ (`before_reasoning`, `after_reasoning`, `reasoning.instructions:->`).
74
+ - Sum of CC of every method/constructor body in every Apex class reached
75
+ through an `apex://` action target.
76
+ - Apex classes referenced by multiple bundles are counted **once** in the
77
+ combined total (de-duplicated by class path).
78
+
79
+ ### What the plugin does NOT count
80
+
81
+ - `else` clauses, `when else` arms, `finally` blocks, `try` itself
82
+ (per standard McCabe).
83
+ - AgentScript constructs the language doesn't have (loops, switch on AS
84
+ side — the AS grammar has no `for`/`while`/`case`).
85
+ - Methods without a body (interface methods, abstract methods).
86
+ - Apex methods reachable only through `flow://` or `prompt://` targets
87
+ (those URIs aren't followed yet).
88
+ - Old-style Bot dialogs or GenAiPlannerBundle action declarations
89
+ (tracked as TODO).
@@ -0,0 +1,112 @@
1
+ # Install
2
+
3
+ There are two install paths depending on the plugin's distribution
4
+ state. As of the date this skill was last updated (2026-05-19), the
5
+ plugin is **not yet published to npm**, so the from-source path is the
6
+ only one that works today.
7
+
8
+ ## Path A — From source (today, pre-publication)
9
+
10
+ The plugin lives at `~/projects/AgentForcePMD/` (or wherever the user
11
+ cloned it). To install it into the `sf` CLI:
12
+
13
+ ```bash
14
+ cd ~/projects/AgentForcePMD
15
+ npm install
16
+ npm run build
17
+ sf plugins link "$(pwd)"
18
+ ```
19
+
20
+ The link survives across `sf` invocations. You only need to rebuild
21
+ (`npm run build`) when source under `src/` changes; the link itself
22
+ remains. See [`upgrade.md`](upgrade.md) for the refresh dance.
23
+
24
+ ### Verify the install
25
+
26
+ ```bash
27
+ sf plugins # should list 'sf-agentpmd X.Y.Z (link)'
28
+ sf agentpmd analyze --help # should render the flag reference
29
+ ```
30
+
31
+ `sf` will emit a one-time warning per process the first time you
32
+ invoke a linked ESM plugin:
33
+
34
+ > Warning: sf-agentpmd is a linked ESM module and cannot be
35
+ > auto-transpiled. Existing compiled source will be used instead.
36
+
37
+ This is benign. It's telling you `sf` is reading `lib/` (the build
38
+ output), not running TypeScript directly. Just remember to
39
+ `npm run build` after `src/` edits.
40
+
41
+ ### Where the linked plugin lives
42
+
43
+ `sf plugins link` records the absolute path under
44
+ `~/.local/share/sf/client/<version>/`. Inspecting:
45
+
46
+ ```bash
47
+ sf plugins --core # nothing — not a core plugin
48
+ sf plugins # sf-agentpmd appears here under its link path
49
+ ```
50
+
51
+ ## Path B — From npm (future, post-publication)
52
+
53
+ When the plugin is published to the npm registry, the standard SF CLI
54
+ install path applies:
55
+
56
+ ```bash
57
+ sf plugins install sf-agentpmd
58
+ ```
59
+
60
+ Verify the same way:
61
+
62
+ ```bash
63
+ sf plugins # should list 'sf-agentpmd X.Y.Z'
64
+ sf agentpmd analyze --help
65
+ ```
66
+
67
+ No build step, no link. Plain npm install under the hood.
68
+
69
+ ## Install the Claude Code skill
70
+
71
+ The plugin bundles this Claude Code skill. After the plugin is installed
72
+ (Path A or Path B), copy the skill tree into `~/.claude/skills/` with:
73
+
74
+ ```bash
75
+ sf agentpmd install-skill
76
+ ```
77
+
78
+ This recursively copies the bundled `skill/` tree (SKILL.md plus the
79
+ `references/` pages) to `~/.claude/skills/agentforcepmd/`. Restart Claude
80
+ Code (or reload skills) to activate it.
81
+
82
+ For an npm install, the bundled source lives at
83
+ `node_modules/sf-agentpmd/skill/`; local-dev contributors can instead
84
+ symlink the in-repo `skill/` directory:
85
+
86
+ ```bash
87
+ ln -sfn ~/projects/AgentForcePMD/skill ~/.claude/skills/agentforcepmd
88
+ ```
89
+
90
+ ## Prerequisites
91
+
92
+ - **Node.js ≥ 20** (the plugin declares `"engines": { "node": ">=20" }`).
93
+ - **`sf` CLI v2.131+** (older versions don't load linked ESM plugins
94
+ reliably).
95
+ - **`@apexdevtools/apex-parser` runtime deps** — auto-installed by `npm
96
+ install`. Includes `antlr4` as a peer.
97
+
98
+ ## Uninstall
99
+
100
+ ```bash
101
+ sf plugins unlink sf-agentpmd # Path A — removes the link, source repo untouched
102
+ sf plugins uninstall sf-agentpmd # Path B — removes the npm-installed copy
103
+ ```
104
+
105
+ ## Common install issues
106
+
107
+ | Symptom | Cause | Fix |
108
+ | --- | --- | --- |
109
+ | `Command sf agentpmd not found` after link | `npm run build` not run, so `lib/` is empty / stale | `cd ~/projects/AgentForcePMD && npm run build` |
110
+ | `Cannot find module '@agentscript/parser-javascript'` | Vendored deps missing — `vendor/` wasn't installed | `cd ~/projects/AgentForcePMD && npm install` (the `file:./vendor/...` deps re-symlink) |
111
+ | `sf plugins link` reports "linked" but command absent | sf CLI cache | `rm -rf ~/.local/share/sf/client/*/node_modules/.cache 2>/dev/null && sf plugins link "$(pwd)"` |
112
+ | Warning about ESM transpilation | Expected — see above | Ignore. |
@@ -0,0 +1,205 @@
1
+ # Making sense of the output
2
+
3
+ The plugin emits one of five surfaces depending on flags. Pick by
4
+ downstream consumer.
5
+
6
+ ## The five surfaces
7
+
8
+ | Surface | Triggered by | Audience |
9
+ | --- | --- | --- |
10
+ | Text | default | Terminal eyeballs |
11
+ | JSON envelope | `--json` | Other SF plugins / `jq` pipelines |
12
+ | Markdown | `--format markdown` | PR descriptions, gists, whitepaper appendices |
13
+ | SARIF | `--format sarif` | GitHub Code Scanning, IDE annotations |
14
+ | CSV | `--format csv` | Spreadsheet pivots |
15
+
16
+ `--json` is special: it wraps the data in the standard SF CLI envelope
17
+ (`{ status, result, warnings }`) and takes precedence over `--format`.
18
+ Use it whenever something other than a human is reading.
19
+
20
+ ## Text output
21
+
22
+ ```
23
+ Analyzing sfdx project /path/to/proj (1 package directory).
24
+ AgentForce PMD — Cyclomatic Complexity (McCabe)
25
+ ============================================================
26
+
27
+ [agent] aiAuthoringBundles/Foo/Foo.agent CC=19
28
+ ------------------------------------------------------------
29
+ start_agent play subtotal CC=19
30
+ reasoning.instructions CC=19 if=5 and=9 or=4
31
+
32
+ Action references
33
+ ----------------------------------------------------------
34
+ pickRandomChoice [apex ] used 1x → apex://Foo_Random
35
+
36
+ Apex backing logic (resolved via apex:// targets)
37
+ ============================================================
38
+
39
+ [apex] classes/Foo_Random.cls class CC=12
40
+ referenced by: aiAuthoringBundles/Foo/Foo.agent
41
+ ------------------------------------------------------------
42
+ List<Result> execute(List<Request> requests) CC=8 ternary=2 ||=1 for=1 &&=2 catch=1
43
+ String freshCookie() CC=1 (base only)
44
+ String abbreviate(String s) CC=3 if=2
45
+
46
+ ============================================================
47
+ CC by location (whitepaper § 7)
48
+ AgentScript: 19 Apex: 12 Combined: 31
49
+ Action declarations: 1 (apex 1, flow 0, prompt 0, unknown 0)
50
+ Action references: 2
51
+ ```
52
+
53
+ ### How to read it
54
+
55
+ - **Header line** confirms what was analyzed (auto-discovered project or
56
+ explicit `--source-dir`). If you see this when you didn't expect
57
+ auto-discovery, you forgot `--source-dir`.
58
+ - **`[agent]` blocks**: one per `.agent` file. The bundle's CC and a
59
+ per-scope breakdown (`start_agent` and each `topic`).
60
+ - Each procedure line:
61
+ `<kind> CC=<n> <contributor breakdown>`
62
+ where contributor breakdown is something like `if=3 and=1`. The CC
63
+ number is the McCabe value (`1 + sum(contributors)`). `(base only)`
64
+ means no control flow; CC = 1.
65
+ - **Action references** lists each declared action with its target kind
66
+ (apex/flow/prompt/unknown) and a usage count (`used Nx`). A `used 0x`
67
+ declaration means the action is defined but not invoked — usually a
68
+ red flag (dead code) unless it's a planner-only action.
69
+ - **`[apex]` blocks**: one per `.cls` file resolved via `apex://`. Lists
70
+ every method and constructor with its signature, CC, and contributor
71
+ breakdown using Apex-flavored short names (`for`, `while`, `do-while`,
72
+ `when`, `catch`, `ternary`, `&&`, `||`).
73
+ - **`CC by location`** is the headline number: `AgentScript`,
74
+ `Apex`, `Combined`. This maps to the whitepaper § 7 framing — same
75
+ agent measured in two different layers, summed for the gross posture.
76
+
77
+ ### TTY-aware rendering
78
+
79
+ When stdout isn't a TTY (piped, redirected, captured by CI), the text
80
+ renderer auto-degrades:
81
+ - Emoji prefixes (`📄`, `📜`) become `[agent]` / `[apex]`.
82
+ - Unicode rules (`═`, `─`) become `=` / `-`.
83
+ - ANSI color is disabled.
84
+
85
+ Force ASCII even on a TTY with `--ascii`. Force color off with
86
+ `--no-color` or `NO_COLOR=1`.
87
+
88
+ ## JSON envelope (`--json`)
89
+
90
+ Standard SF CLI shape:
91
+
92
+ ```json
93
+ {
94
+ "status": 0,
95
+ "result": {
96
+ "files": [ { "path": "...", "procedures": [...], "fileComplexity": 19, ... } ],
97
+ "totalComplexity": 19,
98
+ "totalDeclarations": 1,
99
+ "totalReferences": 2,
100
+ "byTargetKind": { "apex": 1, "flow": 0, "prompt": 0, "utils": 0, "unknown": 0 },
101
+ "apexClasses": [ { "className": "...", "path": "...", "methods": [...], "classComplexity": 12, ... } ],
102
+ "totalApexComplexity": 12,
103
+ "unresolvedApexTargets": []
104
+ },
105
+ "warnings": []
106
+ }
107
+ ```
108
+
109
+ Useful `jq` recipes:
110
+
111
+ ```bash
112
+ # Combined CC for a CI gate
113
+ sf agentpmd analyze --json | jq '.result.totalComplexity + .result.totalApexComplexity'
114
+
115
+ # List apex:// targets that couldn't be resolved
116
+ sf agentpmd analyze --json | jq -r '.result.unresolvedApexTargets[]'
117
+
118
+ # Per-bundle CC table
119
+ sf agentpmd analyze --json | jq -r '.result.files[] | "\(.fileComplexity)\t\(.path)"'
120
+
121
+ # Methods exceeding CC 10
122
+ sf agentpmd analyze --json \
123
+ | jq -r '.result.apexClasses[].methods[] | select(.complexity >= 10) | "\(.complexity)\t\(.signature)"'
124
+ ```
125
+
126
+ ## Markdown (`--format markdown`)
127
+
128
+ Designed for embedding in PR descriptions, gists, Slack snippets,
129
+ whitepaper appendices.
130
+
131
+ - Top: a Mermaid `xychart-beta` bar chart with one bar per `.agent`
132
+ bundle, split into AgentScript CC (red — whitepaper "Reasoning
133
+ Logic") and Apex CC (green — "Deterministic Logic"). Renders
134
+ natively in GitHub, GitLab, and most modern markdown previewers.
135
+ - Followed by a per-bundle table (procedure-by-procedure).
136
+ - Followed by a per-Apex-class table (method-by-method).
137
+ - Final "CC by location" totals row.
138
+
139
+ GFM pipe escaping is applied automatically — `||` short-circuit
140
+ operators in the Apex breakdown render as `\|\|` in cells so they don't
141
+ break the row.
142
+
143
+ Typical use:
144
+
145
+ ```bash
146
+ sf agentpmd analyze --format markdown > report.md
147
+ gh pr comment <pr-number> --body-file report.md
148
+ ```
149
+
150
+ ## SARIF (`--format sarif`)
151
+
152
+ SARIF 2.1.0. One result per procedure + per Apex method. Severity
153
+ driven by `--sarif-warning` (default 10) and `--sarif-error` (default
154
+ 20), matching PMD's `methodReportLevel` convention.
155
+
156
+ Upload to GitHub Code Scanning:
157
+
158
+ ```yaml
159
+ # .github/workflows/agentpmd.yml
160
+ - name: Analyze
161
+ run: sf agentpmd analyze --format sarif > agentpmd.sarif
162
+ - uses: github/codeql-action/upload-sarif@v3
163
+ with:
164
+ sarif_file: agentpmd.sarif
165
+ ```
166
+
167
+ Each result carries `properties.complexity` (numeric) and
168
+ `properties.kind` (`method` / `constructor` for Apex, `before_reasoning`
169
+ / `after_reasoning` / `reasoning_instructions` for AgentScript) so
170
+ follow-on tooling can re-band severity without re-running.
171
+
172
+ Row origins are normalized to 1-based in SARIF regardless of source
173
+ parser (AgentScript CST is 0-based; ANTLR is 1-based for line, 0-based
174
+ for column — the renderer adjusts).
175
+
176
+ ## CSV (`--format csv`)
177
+
178
+ RFC-4180 quoted. One header row + one row per procedure / per Apex
179
+ method.
180
+
181
+ Columns:
182
+
183
+ ```
184
+ type, file, scope_or_class, name, complexity, start_row, start_col, contributors
185
+ ```
186
+
187
+ `type` is either `agent_procedure` or `apex_method`. `contributors`
188
+ is a semicolon-separated `kind=count` list (e.g.
189
+ `if_statement=3;short_circuit_and=1`).
190
+
191
+ Pivot in your favorite spreadsheet on `type` × `file` for a posture
192
+ matrix, or on `kind` (parsed from `contributors`) for a contributor
193
+ breakdown.
194
+
195
+ ## Picking a format — the cheat sheet
196
+
197
+ | Question | Best surface |
198
+ | --- | --- |
199
+ | "What's the CC of this file right now in my terminal?" | text |
200
+ | "I want to gate the PR if combined CC ≥ 80." | `--fail-on 80` (uses text or `--json` for visibility) |
201
+ | "I want PR annotations on the specific high-CC methods." | sarif |
202
+ | "I want to drop a CC table into a PR description." | markdown |
203
+ | "I want to share the numbers in a quick gist." | markdown |
204
+ | "I want to feed the per-method numbers into a spreadsheet." | csv |
205
+ | "I want another tool / script to consume the output." | `--json` |
@@ -0,0 +1,112 @@
1
+ # Upgrade
2
+
3
+ ## Path A — From source (linked checkout)
4
+
5
+ When `sf-agentpmd` is installed via `sf plugins link <repo>`, upgrading
6
+ means pulling new commits and rebuilding `lib/`. The link itself does
7
+ not need to be re-established.
8
+
9
+ ```bash
10
+ cd ~/projects/AgentForcePMD
11
+ git pull --ff-only
12
+ npm install # if dependencies changed
13
+ npm run build
14
+ ```
15
+
16
+ That's it. The next `sf agentpmd analyze` invocation picks up the new
17
+ `lib/`. No `sf plugins link` re-run is needed because the link points
18
+ at the on-disk directory, not at a specific build.
19
+
20
+ If `npm install` reports dependency tree changes that don't match
21
+ `package-lock.json`, that's fine — it usually means upstream patched a
22
+ transitive. To enforce a clean install matching the lockfile:
23
+
24
+ ```bash
25
+ npm ci # exact install per package-lock.json
26
+ npm run build
27
+ ```
28
+
29
+ ### When to also `npm install`
30
+
31
+ | Trigger | Action |
32
+ | --- | --- |
33
+ | Only `src/` changed | `npm run build` |
34
+ | `package.json` or `package-lock.json` changed | `npm install && npm run build` |
35
+ | `vendor/` changed (e.g. agentscript parser refresh) | `npm install && npm run build` |
36
+ | `tsconfig.json` changed | `npm run clean && npm run build` |
37
+
38
+ ## Path B — From npm (published plugin)
39
+
40
+ ```bash
41
+ sf plugins update # bumps every installed sf plugin
42
+ # or, to target this one:
43
+ sf plugins install sf-agentpmd@latest # explicit
44
+ sf plugins install sf-agentpmd@0.3.1 # pin to a specific version
45
+ ```
46
+
47
+ `sf plugins update` is idempotent and safe to run at any time. It
48
+ respects the version range declared at install (defaults to `latest`).
49
+
50
+ To check whether you're on the latest:
51
+
52
+ ```bash
53
+ sf plugins # shows installed version
54
+ npm view sf-agentpmd version # shows current published version
55
+ ```
56
+
57
+ ## Downgrade
58
+
59
+ ```bash
60
+ # Path B: pin to an earlier version
61
+ sf plugins install sf-agentpmd@0.2.0
62
+
63
+ # Path A: git checkout the desired tag, then rebuild
64
+ cd ~/projects/AgentForcePMD
65
+ git checkout v0.2.0
66
+ npm install
67
+ npm run build
68
+ ```
69
+
70
+ ## Switching between linked source and published npm
71
+
72
+ To move from a linked checkout to the published version:
73
+
74
+ ```bash
75
+ sf plugins unlink sf-agentpmd
76
+ sf plugins install sf-agentpmd
77
+ ```
78
+
79
+ To move from the published version back to a linked checkout:
80
+
81
+ ```bash
82
+ sf plugins uninstall sf-agentpmd
83
+ cd ~/projects/AgentForcePMD
84
+ sf plugins link "$(pwd)"
85
+ ```
86
+
87
+ ## Refreshing the Claude Code skill
88
+
89
+ The bundled skill ships inside the plugin, so a plugin upgrade may carry
90
+ new skill content. After upgrading, re-copy the skill tree:
91
+
92
+ ```bash
93
+ sf agentpmd install-skill # re-copies skill/ to ~/.claude/skills/agentforcepmd/
94
+ ```
95
+
96
+ Restart Claude Code (or reload skills) to pick up the new content. If you
97
+ use a local-dev symlink (`~/.claude/skills/agentforcepmd` -> the repo's
98
+ `skill/` directory), no re-copy is needed — a `git pull` updates the
99
+ skill in place.
100
+
101
+ ## Verifying the upgrade
102
+
103
+ Always sanity-check after upgrading:
104
+
105
+ ```bash
106
+ sf agentpmd analyze --help # confirm new flags / removed flags
107
+ sf agentpmd --version 2>/dev/null \
108
+ || sf plugins | grep agentpmd # confirm version bump
109
+ ```
110
+
111
+ If the plugin gained new commands, they appear under the `agentpmd`
112
+ topic. `sf agentpmd` (no subcommand) prompts to pick one.