Update the Compound Engineering plugin content and installers to the upstream v3.15.0 release. - Refresh bundled Compound Engineering agents, skills, references, and helper scripts from upstream v3.15.0. - Add upstream provenance metadata and regression coverage for provenance, agent installation, and skill installation behavior. - Update plugin registration and installer logic to expose the expanded agent and skill catalog. Files changed: .../fusion-plugin-compound-engineering/README.md | 18 +- .../src/__tests__/agent-installation.test.ts | 16 +- .../src/__tests__/skill-installation.test.ts | 19 +- .../src/__tests__/upstream-provenance.test.ts | 62 ++ .../src/agent-installation.ts | 66 +- .../src/agents/ce-adversarial-document-reviewer.md | 12 +- .../src/agents/ce-adversarial-reviewer.md | 12 +- .../agents/ce-agent-native-planning-strategist.md | 71 ++ .../src/agents/ce-best-practices-researcher.md | 23 +- .../src/agents/ce-data-integrity-guardian.md | 4 + .../src/agents/ce-deployment-verification-agent.md | 4 + .../src/agents/ce-figma-design-sync.md | 4 +- .../src/agents/ce-framework-docs-researcher.md | 4 + .../src/agents/ce-git-history-analyzer.md | 2 +- .../src/agents/ce-issue-intelligence-analyst.md | 9 +- .../src/agents/ce-learnings-researcher.md | 16 +- .../agents/ce-pattern-recognition-specialist.md | 4 + .../src/agents/ce-performance-oracle.md | 4 + .../src/agents/ce-pr-comment-resolver.md | 120 +-- .../src/agents/ce-product-lens-reviewer.md | 2 +- .../src/agents/ce-project-standards-reviewer.md | 8 +- .../src/agents/ce-repo-research-analyst.md | 6 +- .../src/agents/ce-scope-guardian-reviewer.md | 6 +- .../src/agents/ce-security-lens-reviewer.md | 2 +- .../src/agents/ce-security-sentinel.md | 4 + .../src/agents/ce-session-historian.md | 14 +- .../src/agents/ce-slack-researcher.md | 25 +- .../src/agents/ce-web-researcher.md | 13 +- .../src/index.ts | 15 +- .../src/skill-installation.ts | 69 +- .../src/skills/ce-brainstorm/SKILL.md | 125 ++- .../references/agents/slack-researcher.md | 127 +++ .../references/brainstorm-sections.md | 161 +++- .../src/skills/ce-brainstorm/references/handoff.md | 114 ++- .../ce-brainstorm/references/html-rendering.md | 253 ++++-- .../ce-brainstorm/references/markdown-rendering.md | 55 +- .../ce-brainstorm/references/synthesis-summary.md | 30 +- .../references/universal-brainstorming.md | 14 +- .../ce-brainstorm/references/visual-probes.md | 128 +++ .../ce-brainstorm/scripts/visual-probe-server.js | 419 ++++++++++ .../src/skills/ce-code-review/SKILL.md | 921 ++++++++++----------- .../references/action-class-rubric.md | 26 + .../references/cross-model-review.md | 63 ++ .../skills/ce-code-review/references/diff-scope.md | 12 +- .../ce-code-review/references/findings-schema.json | 26 +- .../ce-code-review/references/persona-catalog.md | 54 +- .../references/personas/adversarial-reviewer.md | 102 +++ .../references/personas/agent-native-reviewer.md | 173 ++++ .../references/personas/api-contract-reviewer.md | 43 + .../references/personas/correctness-reviewer.md | 43 + .../references/personas/data-migration-reviewer.md | 111 +++ .../personas/deployment-verification-agent.md | 157 ++++ .../personas/julik-frontend-races-reviewer.md | 44 + .../references/personas/learnings-researcher.md | 247 ++++++ .../personas/maintainability-reviewer.md | 68 ++ .../references/personas/performance-reviewer.md | 45 + .../personas/previous-comments-reviewer.md | 59 ++ .../personas/project-standards-reviewer.md | 75 ++ .../references/personas/reliability-reviewer.md | 43 + .../references/personas/security-reviewer.md | 45 + .../references/personas/swift-ios-reviewer.md | 99 +++ .../references/personas/testing-reviewer.md | 43 + .../references/review-output-template.md | 117 +-- .../ce-code-review/references/subagent-template.md | 51 +- .../references/validator-template.md | 6 +- .../scripts/cross-model-adversarial-review.sh | 218 +++++ .../src/skills/ce-commit-push-pr/SKILL.md | 19 +- .../references/pr-description-writing.md | 2 +- .../src/skills/ce-commit/SKILL.md | 4 +- .../src/skills/ce-compound/SKILL.md | 164 +++- .../references/agents/best-practices-researcher.md | 115 +++ .../references/agents/data-integrity-guardian.md | 68 ++ .../references/agents/framework-docs-researcher.md | 93 +++ .../agents/pattern-recognition-specialist.md | 55 ++ .../references/agents/performance-oracle.md | 108 +++ .../references/agents/security-sentinel.md | 91 ++ .../references/agents/session-historian.md | 83 ++ .../scripts/session-history/discover-sessions.sh | 130 +++ .../scripts/session-history/extract-errors.py | 254 ++++++ .../scripts/session-history/extract-metadata.py | 456 ++++++++++ .../scripts/session-history/extract-skeleton.py | 570 +++++++++++++ .../src/skills/ce-debug/SKILL.md | 286 +++++-- .../skills/ce-debug/references/anti-patterns.md | 91 ++ .../skills/ce-debug/references/defense-in-depth.md | 35 + .../references/investigation-techniques.md | 374 +++++++++ .../src/skills/ce-ideate/SKILL.md | 223 ++--- .../agents/issue-intelligence-analyst.md | 200 +++++ .../references/agents/learnings-researcher.md | 247 ++++++ .../references/agents/slack-researcher.md | 127 +++ .../ce-ideate/references/agents/web-researcher.md | 121 +++ .../ce-ideate/references/divergent-ideation.md | 89 ++ .../skills/ce-ideate/references/html-rendering.md | 631 ++++++++++++++ .../ce-ideate/references/ideation-sections.md | 191 +++++ .../ce-ideate/references/markdown-rendering.md | 236 ++++++ .../ce-ideate/references/post-ideation-workflow.md | 246 ++---- .../ce-ideate/references/universal-ideation.md | 24 +- .../src/skills/ce-plan/SKILL.md | 171 ++-- .../agents/agent-native-planning-strategist.md | 62 ++ .../references/agents/architecture-strategist.md | 46 + .../references/agents/best-practices-researcher.md | 114 +++ .../references/agents/data-integrity-guardian.md | 68 ++ .../references/agents/data-migration-reviewer.md | 103 +++ .../agents/deployment-verification-agent.md | 157 ++++ .../references/agents/framework-docs-researcher.md | 93 +++ .../references/agents/git-history-analyzer.md | 40 + .../references/agents/learnings-researcher.md | 247 ++++++ .../agents/pattern-recognition-specialist.md | 55 ++ .../references/agents/performance-oracle.md | 108 +++ .../references/agents/repo-research-analyst.md | 256 ++++++ .../ce-plan/references/agents/security-sentinel.md | 91 ++ .../ce-plan/references/agents/slack-researcher.md | 127 +++ .../references/agents/spec-flow-analyzer.md | 80 ++ .../ce-plan/references/agents/web-researcher.md | 121 +++ .../skills/ce-plan/references/approach-altitude.md | 55 ++ .../ce-plan/references/deepening-workflow.md | 68 +- .../skills/ce-plan/references/html-rendering.md | 253 ++++-- .../ce-plan/references/markdown-rendering.md | 55 +- .../src/skills/ce-plan/references/plan-handoff.md | 81 +- .../src/skills/ce-plan/references/plan-sections.md | 231 +++++- .../skills/ce-plan/references/synthesis-summary.md | 28 +- .../ce-plan/references/universal-planning.md | 9 +- .../src/skills/ce-resolve-pr-feedback/SKILL.md | 12 +- .../references/agents/pr-comment-resolver.md | 56 ++ .../references/evaluation-rubric.md | 106 +++ .../ce-resolve-pr-feedback/references/full-mode.md | 207 ++--- .../references/targeted-mode.md | 24 +- .../ce-resolve-pr-feedback/scripts/get-pr-comments | 13 +- .../scripts/get-thread-for-comment | 11 +- .../src/skills/ce-strategy/SKILL.md | 4 +- .../src/skills/ce-work/SKILL.md | 191 +++-- .../ce-work/references/agents/figma-design-sync.md | 165 ++++ .../skills/ce-work/references/execution-engines.md | 85 ++ .../ce-work/references/non-code-execution.md | 23 + .../ce-work/references/review-findings-followup.md | 104 +++ .../skills/ce-work/references/shipping-workflow.md | 89 +- .../src/skills/ce-work/references/tracker-defer.md | 16 +- .../src/upstream-provenance.ts | 15 + 137 files changed, 12130 insertions(+), 1994 deletions(-) Fusion-Task-Id: FN-7094 Fusion-Task-Lineage: 85df8f84-86f1-496a-953a-e4cf5a9da596 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
215 lines
8.2 KiB
TypeScript
215 lines
8.2 KiB
TypeScript
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { COMPOUND_ENGINEERING_SKILLS } from "./skills.js";
|
|
import { CE_UPSTREAM_PROVENANCE } from "./upstream-provenance.js";
|
|
|
|
/**
|
|
* Physical install of the bundled Compound Engineering skills.
|
|
*
|
|
* EMPIRICAL FINDING (U2): the engine never ingests
|
|
* `PluginSkillContribution.skillFiles` into the set of skills that
|
|
* pi-coding-agent's `DefaultResourceLoader`/`loadSkills` discovers. The
|
|
* contribution only contributes a *name* to `requestedSkillNames`, which the
|
|
* skill-resolver then tries to MATCH against skills already discovered from
|
|
* disk. If no `SKILL.md` for that name was discovered, the requested name
|
|
* resolves to nothing. Therefore a physical install into a discoverable,
|
|
* PLUGIN-LOCAL skills directory is required.
|
|
*
|
|
* This mirrors the cpSync install posture of `installBundledFusionSkill`
|
|
* (packages/cli) but the target is ALWAYS plugin-local — it MUST NOT be a
|
|
* global `<home>/.claude/skills` path (R12/AE2). Existing plugin-local installs
|
|
* are preserved only when their provenance marker matches the current bundled
|
|
* upstream release; stale or unmarked installs are refreshed so users actually
|
|
* receive vendored prompt updates.
|
|
* The installed directory is intended to be wired into a session via
|
|
* `additionalSkillPaths` (engine-side, in later units), keeping discovery
|
|
* scoped to the plugin and never clobbering a user's global install.
|
|
*/
|
|
|
|
export type CeSkillInstallOutcome = "installed" | "refreshed" | "skipped" | "error";
|
|
|
|
export interface CeSkillInstallResult {
|
|
skillId: string;
|
|
sourceDir: string;
|
|
targetDir: string;
|
|
outcome: CeSkillInstallOutcome;
|
|
reason?: string;
|
|
}
|
|
|
|
export interface InstallBundledCeSkillsResult {
|
|
targetRoot: string;
|
|
results: CeSkillInstallResult[];
|
|
}
|
|
|
|
/**
|
|
* Absolute path to the plugin's bundled `src/skills` directory (the pinned
|
|
* source of truth). Resolved relative to this module so it is correct whether
|
|
* running from `src` (tests/dev) or `dist` (build output).
|
|
*/
|
|
export function resolveBundledSkillsRoot(): string {
|
|
const here = fileURLToPath(import.meta.url);
|
|
// src/skill-installation.ts -> src/skills ; dist/skill-installation.js -> the
|
|
// bundled skills live next to source under src/skills, so when running from
|
|
// dist we walk up one and into src/skills.
|
|
const dir = dirname(here);
|
|
const local = resolve(dir, "skills");
|
|
if (existsSync(local)) return local;
|
|
return resolve(dir, "..", "src", "skills");
|
|
}
|
|
|
|
/**
|
|
* Default plugin-local install target. Lives under the plugin package root
|
|
* (`.fusion-ce-skills/`), which is ALWAYS plugin-local and never a global
|
|
* client skills directory. Callers may override via `targetRoot` (e.g. tests),
|
|
* but a guard rejects any target inside a global ".claude"/".codex"/".gemini"
|
|
* skills tree.
|
|
*/
|
|
export function resolveDefaultInstallTargetRoot(): string {
|
|
const here = fileURLToPath(import.meta.url);
|
|
// <pkg>/(src|dist)/skill-installation.* -> <pkg>/.fusion-ce-skills
|
|
return resolve(dirname(here), "..", ".fusion-ce-skills");
|
|
}
|
|
|
|
const GLOBAL_SKILL_DIR_PATTERN = /[\\/]\.(claude|codex|gemini)[\\/]skills([\\/]|$)/;
|
|
|
|
/**
|
|
* Guard: refuse to install into a global client skills directory.
|
|
* This is the AE2 isolation invariant — the global compound-engineering install
|
|
* (if present) must be provably untouched.
|
|
*/
|
|
export function assertPluginLocalTarget(targetRoot: string): void {
|
|
const normalized = resolve(targetRoot);
|
|
if (GLOBAL_SKILL_DIR_PATTERN.test(normalized + sep)) {
|
|
throw new Error(
|
|
`Refusing to install Compound Engineering skills into a global client skills directory: ${normalized}. ` +
|
|
`Install target MUST be plugin-local (never <home>/.claude|.codex|.gemini/skills).`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate that a bundled SKILL.md exists and has a non-empty frontmatter
|
|
* `name:`. A malformed/missing file surfaces a clear error instead of being
|
|
* silently skipped.
|
|
*/
|
|
function assertValidSkillSource(skillId: string, sourceDir: string): void {
|
|
if (!existsSync(sourceDir)) {
|
|
throw new Error(`Bundled skill source directory missing for '${skillId}': ${sourceDir}`);
|
|
}
|
|
const skillMd = join(sourceDir, "SKILL.md");
|
|
if (!existsSync(skillMd)) {
|
|
throw new Error(`Bundled skill '${skillId}' has no SKILL.md at ${skillMd}`);
|
|
}
|
|
const content = readFileSync(skillMd, "utf-8");
|
|
if (!/^---[\s\S]*?\bname\s*:\s*\S/m.test(content)) {
|
|
throw new Error(
|
|
`Bundled skill '${skillId}' SKILL.md at ${skillMd} is missing a frontmatter 'name:' field`,
|
|
);
|
|
}
|
|
}
|
|
|
|
export interface InstallBundledCeSkillsOptions {
|
|
/** Override the install target root (must be plugin-local). */
|
|
targetRoot?: string;
|
|
/** Override the bundled source root (tests). */
|
|
sourceRoot?: string;
|
|
}
|
|
|
|
const SKILL_INSTALL_PROVENANCE_FILE = ".fusion-ce-upstream-provenance.json";
|
|
|
|
function installProvenancePath(targetRoot: string): string {
|
|
return join(targetRoot, SKILL_INSTALL_PROVENANCE_FILE);
|
|
}
|
|
|
|
function isCurrentInstalledProvenance(targetRoot: string): boolean {
|
|
try {
|
|
const marker = JSON.parse(readFileSync(installProvenancePath(targetRoot), "utf-8")) as Partial<
|
|
typeof CE_UPSTREAM_PROVENANCE
|
|
>;
|
|
return (
|
|
marker.releaseTag === CE_UPSTREAM_PROVENANCE.releaseTag &&
|
|
marker.tarballSha256 === CE_UPSTREAM_PROVENANCE.tarballSha256
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function writeInstalledProvenance(targetRoot: string): void {
|
|
mkdirSync(targetRoot, { recursive: true });
|
|
writeFileSync(
|
|
installProvenancePath(targetRoot),
|
|
`${JSON.stringify(
|
|
{
|
|
repo: CE_UPSTREAM_PROVENANCE.repo,
|
|
releaseTag: CE_UPSTREAM_PROVENANCE.releaseTag,
|
|
commit: CE_UPSTREAM_PROVENANCE.commit,
|
|
tarballSha256: CE_UPSTREAM_PROVENANCE.tarballSha256,
|
|
installedAt: new Date().toISOString(),
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Copy each bundled CE skill directory into the plugin-local install target.
|
|
*
|
|
* FNXC:CompoundEngineering 2026-06-26-23:55:
|
|
* Upstream v3.15.0 refreshes must reach existing enabled plugins, not only fresh installs. Refresh stale or unmarked plugin-local copies using a provenance marker while keeping current-version installs idempotent and never touching global client skill dirs.
|
|
*/
|
|
export function installBundledCeSkills(
|
|
options: InstallBundledCeSkillsOptions = {},
|
|
): InstallBundledCeSkillsResult {
|
|
const targetRoot = options.targetRoot
|
|
? resolve(options.targetRoot)
|
|
: resolveDefaultInstallTargetRoot();
|
|
assertPluginLocalTarget(targetRoot);
|
|
|
|
const sourceRoot = options.sourceRoot ? resolve(options.sourceRoot) : resolveBundledSkillsRoot();
|
|
const installIsCurrent = isCurrentInstalledProvenance(targetRoot);
|
|
|
|
const results = COMPOUND_ENGINEERING_SKILLS.map<CeSkillInstallResult>((skill) => {
|
|
const sourceDir = join(sourceRoot, skill.skillId);
|
|
const targetDir = join(targetRoot, skill.skillId);
|
|
try {
|
|
assertValidSkillSource(skill.skillId, sourceDir);
|
|
|
|
if (existsSync(targetDir)) {
|
|
if (installIsCurrent) {
|
|
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "skipped", reason: "current install preserved" };
|
|
}
|
|
rmSync(targetDir, { recursive: true, force: true });
|
|
mkdirSync(targetRoot, { recursive: true });
|
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "refreshed", reason: "stale install refreshed" };
|
|
}
|
|
|
|
mkdirSync(targetRoot, { recursive: true });
|
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "installed" };
|
|
} catch (error) {
|
|
return {
|
|
skillId: skill.skillId,
|
|
sourceDir,
|
|
targetDir,
|
|
outcome: "error",
|
|
reason: error instanceof Error ? error.message : String(error),
|
|
};
|
|
}
|
|
});
|
|
|
|
if (results.every((result) => result.outcome !== "error")) {
|
|
writeInstalledProvenance(targetRoot);
|
|
}
|
|
|
|
return { targetRoot, results };
|
|
}
|
|
|
|
/** True if the given path is absolute and not inside a global client skills dir. */
|
|
export function isPluginLocalPath(p: string): boolean {
|
|
return isAbsolute(p) && !GLOBAL_SKILL_DIR_PATTERN.test(resolve(p) + sep);
|
|
}
|