Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 161 additions & 2 deletions plugin/skills/ase-code-analyze/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: ase-code-analyze
argument-hint: "[--help|-h] [--performance|-p] [--security|-s] [--severity|-S=(LOW|MEDIUM|HIGH)] <source-reference>"
argument-hint: "[--help|-h] [--performance|-p] [--security|-s] [--severity|-S=(LOW|MEDIUM|HIGH)] [--quick|-Q] <source-reference>"
description: >
Analyze the source code for problems in either the logic and
semantics and its related control flow, performance and efficiency,
Expand All @@ -10,6 +10,7 @@ disable-model-invocation: false
effort: high
allowed-tools:
- "Agent"
- "Bash"
- "Glob"
---

Expand All @@ -23,7 +24,7 @@ Analyze Source Code

<expand name="getopt"
arg1="ase-code-analyze"
arg2="--performance|-p --security|-s --severity|-S=(LOW|MEDIUM|HIGH)">
arg2="--performance|-p --security|-s --severity|-S=(LOW|MEDIUM|HIGH) --quick|-Q">
$ARGUMENTS
</expand>

Expand Down Expand Up @@ -204,6 +205,164 @@ problems in *performance* and *efficiency*, or problems in *security*.
followed by one `{ command: "set", key: "ase-issue-P<n/>", val:
"<title/>: <description/>" }` entry per reported problem.

<if condition="<getopt-option-quick/> is equal `true` and at least one problem survived the severity floor">

The `--quick`/`-Q` flag turns this analysis into a *one-shot*
`analyze → resolve → implement → verify` pipeline that resolves
the surviving problems fully autonomously, *without any user
interaction*. Problems whose `severity` is `ACCEPTED` are
*excluded* from auto-resolution: they document deliberately
accepted trade-offs, so "fixing" them would undo reviewed
decisions -- they stay reported and persisted only. Let <count/>
be the number of surviving *non-ACCEPTED* problems and
<accepted-count/> the number of surviving `ACCEPTED` problems,
and keep the surviving <problems/> entries (each with its `file`,
`title`, and `description` field) available for the sub-steps
below. If <count/> is `0`, *skip* this entire one-shot block and
continue with the final hint <template/> below.

First, announce the one-shot phase with the following <template/>:

<template>
<ase-tpl-bullet-secondary/> **ONE-SHOT RESOLUTION** (<count/> problem(s), <accepted-count/> ACCEPTED skipped)
</template>

Then perform the following sub-steps and finally *STOP* the entire
flow (do *not* emit the final hint <template/> below):

1. *Guard and snapshot*: with the `Bash` tool, run
`git rev-parse --is-inside-work-tree` to check whether the
target is a Git working tree. If it fails (no Git repository),
worktree isolation is impossible, so *skip* sub-steps 3-6 and
instead resolve each cluster of sub-step 2 *sequentially* --
one non-isolated `Agent` invocation at a time, in ascending
order, each with the sub-step 3 prompt but *without* the
`isolation` parameter, the seeding instruction, and the diff
return (the fixes land in the live tree directly); for each
problem, record its <outcome/> as `resolved` and its changed
files <files/> (from `git diff --name-only` taken immediately
before and after its cluster's `Agent`) -- then continue with
sub-step 7. Otherwise, capture the repository root
`git rev-parse --show-toplevel` as <live-root/> and
`git worktree list --porcelain` as <worktrees-before/> for the
cleanup in sub-step 6.

2. *Cluster by file*: group the <count/> problems by their `file`
field -- problems sharing the same file form *one* cluster
(resolving same-file problems in separate parallel worktrees
would guarantee merge conflicts). Order the clusters by their
lowest contained `P<n/>` and let <cluster-count/> be the number
of clusters, <cluster-index/> the 1-based cluster number, and
<cluster-problems/> the ascending, comma-separated `P<n/>` list
of a cluster.

3. *Resolve in parallel (background)*: for *every* cluster, invoke
the following tool once, emitting *all* invocations *in one
single message* so they run in *parallel*, each in its *own*
isolated Git worktree:

```text
Agent(
description: "Resolve <cluster-problems/> (<cluster-index/>/<cluster-count/>)",
subagent_type: "general-purpose",
isolation: "worktree",
run_in_background: true,
prompt: "First seed this worktree with the live working tree's uncommitted state: run `git -C \"<live-root/>\" diff HEAD | git apply --whitespace=nowarn` (skip when that diff is empty), then `git add -A && git commit -m seed` (skip the commit when nothing is staged). Then, for each of <cluster-problems/> in ascending order, call the tool Skill(skill: \"ase:ase-code-resolve\", args: \"-Q P<n/>\") to fully resolve AND implement that issue in this worktree. When finished, run `git add -A` followed by `git diff --cached`, and return ONLY that complete unified diff as your final message (an empty string if nothing changed)."
)
```

The background execution keeps the long fan-out interruptible
and observable: *each time* one of these `Agent` invocations
completes, immediately output one progress line with the
following <template/>, then keep waiting until *all* clusters
have returned. Collect each returned unified diff, keyed by its
cluster.

<template>
<ase-tpl-bullet-secondary/> **RESOLVED** (<cluster-index/>/<cluster-count/>): <cluster-problems/>
</template>

4. *Reconcile sequentially -- always merge, never prompt*: with the
`Bash` tool, process the collected cluster diffs in ascending
cluster order. For each *non-empty* diff, write it to a
temporary file and apply it with `git apply --whitespace=nowarn
<patch-file/>` -- deliberately *without* `--index`: staging
remains with the user. On success, record each contained
problem's <outcome/> as `merged` and its changed files <files/>
(the `+++ b/<path>` headers of its diff). On failure, retry
with `git apply --3way --whitespace=nowarn <patch-file/>`. If
the three-way attempt also fails or leaves Git conflict markers
in any touched file, or the diff is empty, or its `Agent`
returned nothing, do *not* stop -- instead add the cluster's
problems to a *retry set* (a possibly marker-littered file is
repaired there).

5. *Retry conflicts sequentially*: for each `P<n/>` in the retry
set, in ascending order and strictly *one at a time*, set
<title/> and <description/> to that problem's `title` and
`description` fields (from <problems/>) and invoke a
*non-isolated* `Agent` against the now-updated live working
tree:

```text
Agent(
description: "Re-resolve P<n/> (sequential)",
subagent_type: "general-purpose",
run_in_background: false,
prompt: "Call the tool Skill(skill: \"ase:ase-code-resolve\", args: \"-Q <title/> — <description/>\") to fully resolve AND implement that problem directly in the current working tree. The affected files may still contain Git conflict markers from a failed three-way merge -- remove any such markers as part of the resolution."
)
```

Because each such `Agent` edits the live tree directly and
sequentially, its change always integrates cleanly, so *all*
<count/> problems end up merged. Record that problem's
<outcome/> as `re-resolved` and its changed files <files/>
(from `git diff --name-only` taken immediately before and after
that single `Agent`).

6. *Remove the worktrees*: with the `Bash` tool, compare the current
`git worktree list --porcelain` against <worktrees-before/>; for
*each* newly added worktree, run `git worktree remove --force
<path/>`, then run `git worktree prune` to drop any remaining
administrative references.

7. *Verify and repair*: the per-worktree resolutions ran in `dry`
mode (no verification), so the *merged result* MUST now be
verified centrally. Determine the project's canonical
*formatter*, *build*, and *test* commands from its build
configuration (e.g. npm `package.json` scripts, `Makefile`,
Gradle `spotlessApply`/`build`/`test`, Cargo, etc). With the
`Bash` tool run, in this order: the formatter (if any), the
build, and the test suite. If any of them fails, hand the
*complete* failure output to one *non-isolated*
`Agent(subagent_type: "general-purpose", run_in_background:
false)` with the prompt to repair exactly those failures in the
current working tree without touching unrelated user changes,
then re-run the failed commands. Perform at most *two* such
repair rounds. Set <verify/> to `green` when everything passes,
or to a short failure summary when failures remain. Do *not*
stage or commit anything -- staging remains with the user.

8. *Report the per-problem results*: for *every* auto-resolved
problem `P<n/>`, in ascending order, output the following
<template/>, where <marker/> is `✓` when its <outcome/> is
`merged` or `resolved` and `↻` when its <outcome/> is
`re-resolved`, and <files/> is the comma-separated list of its
changed files (or `no changes` when empty):

<template>
<ase-tpl-bullet-signal/> **P<n/>** <marker/> *<outcome/>*: <files/>
</template>

9. Finally, output the following summary <template/> and then *STOP*
the entire flow (do *not* emit the final hint <template/> below):

<template>
⧉ **ASE**: ↪ hint: **one-shot `-Q`: auto-resolved <count/> problem(s) (<accepted-count/> ACCEPTED skipped), verification: <verify/> -- staging remains with you.**
</template>

</if>

Finally, give a final hint by expanding the following (which,
depending on the configured <ase-guidance-level/>, may expand into
nothing and hence emit no output at all):
Expand Down
28 changes: 28 additions & 0 deletions plugin/skills/ase-code-analyze/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[`--performance`|`-p`]
[`--security`|`-s`]
[`--severity`|`-S`=(`LOW`|`MEDIUM`|`HIGH`)]
[`--quick`|`-Q`]
*source-reference*

## DESCRIPTION
Expand Down Expand Up @@ -38,6 +39,26 @@ severity* order `HIGH`, `MEDIUM`, `LOW`, `ACCEPTED` - keeping the
`file`/`line` order within the same severity - and are renumbered
contiguously as `P<n>`, so `P1` is the most severe problem.

The `--quick`|`-Q` option turns the analysis into a fully autonomous
one-shot `analyze → resolve → implement → verify` pipeline: after
reporting, it groups the surviving problems into *per-file clusters*
(same-file problems are never resolved in parallel), dispatches one
worktree-isolated `ase-code-resolve -Q` sub-agent per cluster in
parallel (in the background, with a progress line per returning
cluster; each worktree is first *seeded* with the live tree's
uncommitted state so its diff applies cleanly), then reconciles all
resulting diffs into the working tree (plain `git apply`, `--3way` as
fallback, and a conflicting cluster is re-resolved sequentially so that
*all* problems are merged), removes the temporary worktrees again, and
finally runs the project's formatter, build, and test suite centrally
on the merged result, with up to two autonomous repair rounds for any
failures. `ACCEPTED` problems document deliberately accepted trade-offs
and are *excluded* from auto-resolution. Nothing is staged or
committed -- staging remains with the user. It composes with
`--severity` (only surviving findings are resolved) and requires a Git
repository (otherwise the clusters are resolved sequentially without
worktree isolation).

The skill investigates the code base silently, reports each detected
problem as a `PROBLEM` entry with severity (`LOW`, `MEDIUM`, `HIGH`) and
inline file/line references (in the performance lens, each entry
Expand Down Expand Up @@ -83,6 +104,13 @@ Analyze a directory, reporting only `MEDIUM` and `HIGH` problems:
❯ /ase-code-analyze -S MEDIUM src/handlers/
```

Analyze a directory and, in one non-interactive shot, auto-resolve and
implement every `HIGH` finding:

```text
❯ /ase-code-analyze -Q -S HIGH src/handlers/
```

## SEE ALSO

[`ase-code-resolve`](../ase-code-resolve/help.md), [`ase-code-refactor`](../ase-code-refactor/help.md), [`ase-code-lint`](../ase-code-lint/help.md),
Expand Down