@lifeaitools/rdc-skills 0.35.3 → 0.35.5
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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +26 -0
- package/commands/build.md +19 -0
- package/commands/deploy.md +20 -1
- package/commands/fixit.md +17 -0
- package/commands/flow.md +94 -0
- package/commands/mode.md +6 -0
- package/commands/release.md +11 -0
- package/guides/agent-bootstrap.md +248 -204
- package/hooks/foreground-process-gate.js +22 -64
- package/package.json +2 -2
- package/scripts/install-rdc-skills.js +11 -3
- package/skills/architecture-reviewer/SKILL.md +1 -0
- package/skills/behavior-audit/SKILL.md +1 -0
- package/skills/brochure/SKILL.md +1 -0
- package/skills/brochurify/SKILL.md +1 -0
- package/skills/clean-code-analyzer/SKILL.md +1 -0
- package/skills/convert/SKILL.md +1 -0
- package/skills/edit/SKILL.md +12 -0
- package/skills/env/SKILL.md +1 -0
- package/skills/extract-verifier-rules/SKILL.md +1 -0
- package/skills/fs-mcp/SKILL.md +1 -0
- package/skills/help/SKILL.md +1 -0
- package/skills/lifeai-brochure-author/SKILL.md +1 -0
- package/skills/new-model/SKILL.md +1 -0
- package/skills/package-design/SKILL.md +1 -0
- package/skills/pattern-advisor/SKILL.md +1 -0
- package/skills/pattern-refactoring-guide/SKILL.md +1 -0
- package/skills/refactor/SKILL.md +1 -0
- package/skills/regen-media/SKILL.md +1 -0
- package/skills/solid-validator/SKILL.md +1 -0
- package/skills/terminal-config/SKILL.md +1 -0
- package/skills/testing-strategy/SKILL.md +1 -0
- package/tests/foreground-process-gate.test.mjs +114 -0
- package/tests/harness-gates.test.mjs +9 -27
- package/tests/install-rdc-skills.test.mjs +91 -1
|
@@ -13,6 +13,7 @@ description: >-
|
|
|
13
13
|
rdc:review step 8b+, or standalone before merging a new package/module.
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
16
17
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
17
18
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
18
19
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -3,6 +3,7 @@ name: behavior-audit
|
|
|
3
3
|
description: "Usage `rdc:behavior-audit <report-dir> [--since-days N] [--latest N] [--reprocess]` — produces a bounded, redacted Claude/Codex transcript evidence bundle, incrementally skips completed transcript hashes, and aligns candidate behavior problems to shared truth-governance rules."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **OUTPUT CONTRACT:** Begin and end with the same checklist. Do not call an audit clean, complete, or compliant without the evidence-bundle manifest and an independent validator decision.
|
|
7
8
|
|
|
8
9
|
# rdc:behavior-audit — Cross-Engine Truth and Behavior Audit
|
package/skills/brochure/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: brochure
|
|
|
3
3
|
description: "Usage `rdc:brochure <input> [--out <path>] [--template <name>] [--format Letter|A4]` — Turn a zip, folder, HTML file, URL, or markdown folder into a print-quality PDF brochure via Puppeteer. Auto-detects print-variant HTML, honors @page CSS, falls back to a Studio-token-aware template when no HTML exists."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -12,6 +12,7 @@ triggers:
|
|
|
12
12
|
- monkey_dispatch payload with skill="brochurify"
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
15
16
|
# rdc:brochurify Orchestrator
|
|
16
17
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
17
18
|
> Report brochure job state, artifacts, and blockers directly; do not dump raw tool logs.
|
|
@@ -8,6 +8,7 @@ description: >-
|
|
|
8
8
|
mechanical AST checks, not LLM judgment — see `scripts/clean-code-score.mjs`.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
11
12
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
13
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
14
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
package/skills/convert/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: convert
|
|
|
3
3
|
description: "Usage `rdc:convert <input> [--out <dir>] [--to markdown|word] [--images assets|base64|s3]` — Convert .docx/.pptx/.ppt → Markdown (Word OMML equations as KaTeX TeX, tables, images) or Markdown → Word via the build-corpus CLI from npm `regen.mde`. Portable: runs in any session that can reach npm — Claude Code CLI, Codex, and claude.ai can fetch + run it. Use whenever the user asks to convert an Office document, build a Markdown corpus from .docx/.pptx, turn Markdown into a .docx, or open the report in the regen-mde editor on Windows."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
# rdc:convert — Office ↔ Markdown conversion (build-corpus) + regen-mde editor
|
|
7
8
|
|
|
8
9
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
package/skills/edit/SKILL.md
CHANGED
|
@@ -14,6 +14,18 @@ description: "Usage `rdc:edit <site|brand|route|file>` — open the local websit
|
|
|
14
14
|
|
|
15
15
|
# rdc:edit — Local Website Editor Launcher
|
|
16
16
|
|
|
17
|
+
**This skill is the designated handler `rdc-harness` dispatches to.**
|
|
18
|
+
`packages/work/src/editors.mjs`'s `BY_CLASS` table maps `site-html`/`site-ts`
|
|
19
|
+
product classes to editor id `'rdc:edit'` — when an agent calls
|
|
20
|
+
`node C:/Dev/rdc-harness/bin/rdc-harness.mjs edit <slug>` against a website
|
|
21
|
+
target, its receipt names `editorId: 'rdc:edit'` and an `editableBoundary`.
|
|
22
|
+
This skill IS that handler: resolve the same target, launch/reuse the editor
|
|
23
|
+
host, use the harness's `editableBoundary` as the save boundary if the call
|
|
24
|
+
originated from a harness `edit` receipt. Every other product class
|
|
25
|
+
(`app`/`package`/`mcp`/`model`/`artifact`) maps to editor id `'source'` — a
|
|
26
|
+
boundary-checked file save with no live/co-editing surface at all; that gap
|
|
27
|
+
is real and unbuilt, not something this skill covers.
|
|
28
|
+
|
|
17
29
|
## When to Use
|
|
18
30
|
- The user wants to open a site, brand, route, or file in the local editor app
|
|
19
31
|
- The user says "open this in the editor" or asks for the editor-host workflow
|
package/skills/env/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: env
|
|
|
3
3
|
description: "Usage `rdc:env [status|install|repair|update]` — Manage the LIFEAI environment harness: check status, install on a fresh box, repair broken services, or pull updates. Reads $LIFEAI_ENV/manifest.json as the source of truth. Use when: 'check the environment', 'install environment', 'repair environment', 'update environment', 'setup env', 'fix env', 'env status', or after a reboot/GPU crash."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -12,6 +12,7 @@ triggers:
|
|
|
12
12
|
- nightly cron at 3:00 AM PT
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
15
16
|
# rdc:extract-verifier-rules
|
|
16
17
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
17
18
|
> Return candidate rules, evidence, and PR status directly; do not dump raw tool logs.
|
package/skills/fs-mcp/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: fs-mcp
|
|
|
3
3
|
description: "Usage `rdc:fs-mcp <task>` — Use the File System MCP bridge for live repo reads, safe writes, cloud-to-local ingest, and GitHub-branch imports into a dirty local monorepo. Use when Claude.ai, Cowork, or CLI agents need fs_read/fs_write/fs_import_git_files guidance."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
package/skills/help/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: help
|
|
|
3
3
|
description: "Usage `rdc:help` — Show all MCP skills with usage, requirements, slash forms, and codeflow status. Reads `.claude-plugin/plugin.json` skills_meta (single source of truth). Call when unsure which skill to use or what args it takes."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
|
|
@@ -20,6 +20,7 @@ required_validators:
|
|
|
20
20
|
blocking: true
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
23
24
|
# LIFEAI Brochure Authoring Contract
|
|
24
25
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
25
26
|
> Return the authored JSX guidance or verification result directly; do not dump raw tool logs.
|
|
@@ -18,6 +18,7 @@ description: >
|
|
|
18
18
|
Trigger when asked to create, register, or check whether a reference model exists.
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
21
22
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
22
23
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
23
24
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -6,6 +6,7 @@ description: >-
|
|
|
6
6
|
and sit at the right size.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
9
10
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
10
11
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
11
12
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -8,6 +8,7 @@ description: >-
|
|
|
8
8
|
only; never rewrites code itself.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
11
12
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
13
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
14
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -6,6 +6,7 @@ description: >-
|
|
|
6
6
|
concrete before/after refactor plan. Produces a plan, does not apply it.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
9
10
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
10
11
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
11
12
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
package/skills/refactor/SKILL.md
CHANGED
|
@@ -3,6 +3,7 @@ name: refactor
|
|
|
3
3
|
description: "Usage `rdc:refactor <epic-id|topic> --takeover <reason>` — Governed consolidation and implementation of a cross-cutting refactor. Authorizes an explicit takeover, preserves evidence, re-parents relevant work through the consolidation RPC, isolates every writer, and uses the same review, validator, landing, and delivery gates as rdc:build."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **OUTPUT CONTRACT:** `guides/output-contract.md`
|
|
7
8
|
|
|
8
9
|
# rdc:refactor
|
|
@@ -3,6 +3,7 @@ name: regen-media
|
|
|
3
3
|
description: "Usage `rdc:regen-media <generate|edit|upscale|upload> <brief-or-path>` - Primary image-generation and Regen Media asset workflow. Use for image generation, image editing/upscaling, GPT Image/gpt-image-2 requests, Codex built-in image_gen, and uploading finished images to regen-media/R2. The default path is keyless local Codex gpt-image-2 via the built-in image_gen tool; server-side regen-media MCP/API generation is fallback only."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -8,6 +8,7 @@ description: >-
|
|
|
8
8
|
`scripts/solid-score.mjs` for the mechanism.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
11
12
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
13
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
14
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -3,6 +3,7 @@ name: terminal-config
|
|
|
3
3
|
description: "Usage `rdc:terminal-config <task>` — read and safely modify Windows Terminal settings, shell profiles, and agent startup sequencing without relying on machine-specific paths."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
6
7
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
8
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
9
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -8,6 +8,7 @@ description: >-
|
|
|
8
8
|
covers whether a surface's behavior is actually provable, and how.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
> If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `.rdc/guides/agent-bootstrap.md`) — this is also where the global rdc-harness-use policy for create/open/build/deploy work lives.
|
|
11
12
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
13
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
14
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* foreground-process-gate.js — narrowed to Playwright headed/UI-mode only
|
|
4
|
+
* (epic 688ad6da WP-4, lifeai-env). Four window-focus/process-launch checks
|
|
5
|
+
* (checkWindowFocusApi, checkPowerShell, checkCmdStart, checkDirectShellLaunch)
|
|
6
|
+
* were removed; the replacement is lifeai-env's lib/TermLaunch.psm1 caller-
|
|
7
|
+
* logging, which lives in a different repo and is not this file's job to
|
|
8
|
+
* re-implement or re-check.
|
|
9
|
+
*
|
|
10
|
+
* This file's job is now to prove two things stay true going forward:
|
|
11
|
+
* 1. The four retired checks no longer block anything -- a real regression
|
|
12
|
+
* here would silently reintroduce ceremony an operator explicitly
|
|
13
|
+
* retired, with no other test anywhere positioned to catch it.
|
|
14
|
+
* 2. checkPlaywright survives untouched (Design Decision D3) -- this is a
|
|
15
|
+
* SEPARATE safety property, not part of the terminal-launch
|
|
16
|
+
* consolidation, and narrowing the file must never have narrowed this.
|
|
17
|
+
*
|
|
18
|
+
* Run: node tests/foreground-process-gate.test.mjs
|
|
19
|
+
*/
|
|
20
|
+
import { dirname, join, resolve } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { spawnSync } from 'node:child_process';
|
|
23
|
+
|
|
24
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const REPO_ROOT = resolve(__dirname, '..');
|
|
26
|
+
const HOOK = join(REPO_ROOT, 'hooks', 'foreground-process-gate.js');
|
|
27
|
+
|
|
28
|
+
const failures = [];
|
|
29
|
+
function assert(name, condition, detail = '') {
|
|
30
|
+
if (!condition) failures.push(`${name}${detail ? `: ${detail}` : ''}`);
|
|
31
|
+
else process.stdout.write(` ok ${name}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function runGate(command) {
|
|
35
|
+
const res = spawnSync(process.execPath, [HOOK], {
|
|
36
|
+
input: JSON.stringify({ tool_name: 'Bash', tool_input: { command } }),
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
});
|
|
39
|
+
const blocked = res.status === 1;
|
|
40
|
+
return { blocked, stdout: res.stdout, status: res.status };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ===========================================================================
|
|
44
|
+
// 1. The four retired checks no longer block anything.
|
|
45
|
+
// ===========================================================================
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
const r = runGate('Start-Process -FilePath node.exe -ArgumentList "server.js"');
|
|
49
|
+
assert('Start-Process with no -WindowStyle Hidden no longer blocks (checkPowerShell retired)',
|
|
50
|
+
r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
const r = runGate('cmd /c start node.exe server.js');
|
|
55
|
+
assert('cmd /c start without /min no longer blocks (checkCmdStart retired)',
|
|
56
|
+
r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
{
|
|
60
|
+
const r = runGate('powershell.exe -File C:\\Dev\\some-script.ps1');
|
|
61
|
+
assert('bare PowerShell .ps1 launch no longer blocks (checkDirectShellLaunch retired)',
|
|
62
|
+
r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
const r = runGate('[Win32]::SetForegroundWindow($handle)');
|
|
67
|
+
assert('a window-focus Win32 API call no longer blocks (checkWindowFocusApi retired)',
|
|
68
|
+
r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ===========================================================================
|
|
72
|
+
// 2. checkPlaywright survives, untouched -- a separate safety property.
|
|
73
|
+
// ===========================================================================
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
const r = runGate('npx playwright test --headed');
|
|
77
|
+
assert('Playwright --headed still blocks',
|
|
78
|
+
r.blocked === true, `status=${r.status} stdout=${r.stdout}`);
|
|
79
|
+
assert('Playwright --headed block message names the right reason',
|
|
80
|
+
/must run headless/.test(r.stdout), r.stdout);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
const r = runGate('PWDEBUG=1 npx playwright test');
|
|
85
|
+
assert('Playwright PWDEBUG=1 still blocks',
|
|
86
|
+
r.blocked === true, `status=${r.status} stdout=${r.stdout}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
const r = runGate('npx playwright show-report');
|
|
91
|
+
assert('Playwright show-report still blocks',
|
|
92
|
+
r.blocked === true, `status=${r.status} stdout=${r.stdout}`);
|
|
93
|
+
assert('Playwright show-report block message names the right reason',
|
|
94
|
+
/launches foreground UI/.test(r.stdout), r.stdout);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
{
|
|
98
|
+
const r = runGate('npx playwright test --reporter=list');
|
|
99
|
+
assert('an ordinary headless Playwright run is NOT blocked',
|
|
100
|
+
r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
{
|
|
104
|
+
const r = runGate('git status');
|
|
105
|
+
assert('an unrelated ordinary command passes clean', r.blocked === false, `status=${r.status} stdout=${r.stdout}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ===========================================================================
|
|
109
|
+
if (failures.length > 0) {
|
|
110
|
+
console.error('\nforeground-process-gate tests — FAIL\n');
|
|
111
|
+
for (const f of failures) console.error(` - ${f}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log('\nforeground-process-gate tests — PASS');
|
|
@@ -146,34 +146,16 @@ const WI = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// ===========================================================================
|
|
149
|
-
// 2. foreground-process-gate.js
|
|
149
|
+
// 2. foreground-process-gate.js — moved to its own file, tests/foreground-process-gate.test.mjs
|
|
150
|
+
//
|
|
151
|
+
// Narrowed 2026-08-26 (epic 688ad6da WP-4, lifeai-env): the window-focus/
|
|
152
|
+
// process-launch checks this section used to assert (SetForegroundWindow
|
|
153
|
+
// blocking, hidden/minimized allowances) were retired — that whole class is
|
|
154
|
+
// now caller-logged by lifeai-env's lib/TermLaunch.psm1, not hard-blocked
|
|
155
|
+
// here. The dedicated test file covers what remains (checkPlaywright) plus
|
|
156
|
+
// asserts the four retired checks stay retired, so removing this embedded
|
|
157
|
+
// section is not a coverage loss — see that file instead.
|
|
150
158
|
// ===========================================================================
|
|
151
|
-
{
|
|
152
|
-
const focusPayload = {
|
|
153
|
-
tool_input: {
|
|
154
|
-
command: "powershell -NoProfile -Command \"Add-Type '[DllImport(\\\"user32.dll\\\")] public static extern bool SetForegroundWindow(System.IntPtr hWnd);'\"",
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
const r = runHook('foreground-process-gate.js', focusPayload, {});
|
|
158
|
-
assert('FPG blocks SetForegroundWindow focus API', r.status === 1, `status=${r.status} ${r.stdout}${r.stderr}`);
|
|
159
|
-
assert('FPG block mentions window focus operations', /Window focus\/restore\/minimize\/collapse/.test(r.stdout + r.stderr));
|
|
160
|
-
|
|
161
|
-
const hiddenPayload = {
|
|
162
|
-
tool_input: {
|
|
163
|
-
command: 'powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -File ".\\\\scripts\\\\helper.ps1"',
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
const h = runHook('foreground-process-gate.js', hiddenPayload, {});
|
|
167
|
-
assert('FPG allows hidden PowerShell helper', h.status === 0, `status=${h.status} ${h.stdout}${h.stderr}`);
|
|
168
|
-
|
|
169
|
-
const minimizedPayload = {
|
|
170
|
-
tool_input: {
|
|
171
|
-
command: 'Start-Process powershell.exe -WindowStyle Minimized -ArgumentList "-NoProfile"',
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
const m = runHook('foreground-process-gate.js', minimizedPayload, {});
|
|
175
|
-
assert('FPG allows minimized Start-Process without focus APIs', m.status === 0, `status=${m.status} ${m.stdout}${m.stderr}`);
|
|
176
|
-
}
|
|
177
159
|
|
|
178
160
|
// ===========================================================================
|
|
179
161
|
// 3. post-tool-batch-gate.js
|
|
@@ -44,7 +44,22 @@ assert.match(
|
|
|
44
44
|
const skillCount = Array.isArray(plugin.skills_meta)
|
|
45
45
|
? plugin.skills_meta.length
|
|
46
46
|
: Object.keys(plugin.skills_meta || {}).length;
|
|
47
|
-
|
|
47
|
+
// A hardcoded magic number here (previously 36, silently stale against the
|
|
48
|
+
// real 43) rots the instant a skill is added or removed and stops proving
|
|
49
|
+
// anything — it only proves someone remembered to bump a number. Assert
|
|
50
|
+
// against the actual skills/ directory instead: every dir containing a
|
|
51
|
+
// SKILL.md must be represented in plugin.json's skills_meta, and vice versa.
|
|
52
|
+
const skillsDir = join(REPO_ROOT, 'skills');
|
|
53
|
+
const realSkillDirs = require('node:fs')
|
|
54
|
+
.readdirSync(skillsDir, { withFileTypes: true })
|
|
55
|
+
.filter((e) => e.isDirectory() && existsSync(join(skillsDir, e.name, 'SKILL.md')))
|
|
56
|
+
.map((e) => e.name)
|
|
57
|
+
.sort();
|
|
58
|
+
assert.equal(
|
|
59
|
+
skillCount,
|
|
60
|
+
realSkillDirs.length,
|
|
61
|
+
`plugin.json skills_meta (${skillCount}) must match the actual skill directories on disk (${realSkillDirs.length}): ${realSkillDirs.join(', ')}`,
|
|
62
|
+
);
|
|
48
63
|
assert.match(
|
|
49
64
|
source,
|
|
50
65
|
/Available MCP skills.*\/rdc:\* command shorthands/,
|
|
@@ -97,6 +112,81 @@ try {
|
|
|
97
112
|
rmSync(codexSkills, { recursive: true, force: true });
|
|
98
113
|
}
|
|
99
114
|
|
|
115
|
+
// Regression for the 2026-08-23 incident: a marketplace clone with ZERO real
|
|
116
|
+
// local edits — only a stray untracked file — sat 39+ commits behind for over
|
|
117
|
+
// a week because `git status --porcelain` (which also reports untracked
|
|
118
|
+
// files) was treated as "has local changes, never touch it". `git reset
|
|
119
|
+
// --hard` never touches untracked files, so an untracked file is irrelevant
|
|
120
|
+
// to whether the sync is safe. This builds a real origin + clone pair,
|
|
121
|
+
// reproduces the exact scenario, and asserts the clone actually advances.
|
|
122
|
+
const { syncMarketplaceCheckout } = require(script);
|
|
123
|
+
const gitTestRoot = mkdtempSync(join(tmpdir(), 'rdc-marketplace-sync-'));
|
|
124
|
+
try {
|
|
125
|
+
const originDir = join(gitTestRoot, 'origin');
|
|
126
|
+
const cloneDir = join(gitTestRoot, 'clone');
|
|
127
|
+
const runGit = (cwd, args) => {
|
|
128
|
+
const res = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
129
|
+
assert.equal(res.status, 0, `git ${args.join(' ')} failed: ${res.stderr}`);
|
|
130
|
+
return res.stdout.trim();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
mkdirSync(originDir, { recursive: true });
|
|
134
|
+
runGit(originDir, ['init', '--initial-branch=master']);
|
|
135
|
+
runGit(originDir, ['config', 'user.email', 'test@example.com']);
|
|
136
|
+
runGit(originDir, ['config', 'user.name', 'Test']);
|
|
137
|
+
writeFileSync(join(originDir, 'file.txt'), 'v1\n');
|
|
138
|
+
runGit(originDir, ['add', '.']);
|
|
139
|
+
runGit(originDir, ['commit', '-m', 'v1']);
|
|
140
|
+
|
|
141
|
+
runGit(gitTestRoot, ['clone', originDir, cloneDir]);
|
|
142
|
+
|
|
143
|
+
// Advance origin so the clone is genuinely behind.
|
|
144
|
+
writeFileSync(join(originDir, 'file.txt'), 'v2\n');
|
|
145
|
+
runGit(originDir, ['add', '.']);
|
|
146
|
+
runGit(originDir, ['commit', '-m', 'v2']);
|
|
147
|
+
const originHead = runGit(originDir, ['rev-parse', 'HEAD']);
|
|
148
|
+
|
|
149
|
+
// The exact reproduction: a stray UNTRACKED file, nothing modified/staged.
|
|
150
|
+
writeFileSync(join(cloneDir, 'some-local-cruft.cjs'), 'module.exports = {};\n');
|
|
151
|
+
assert.match(
|
|
152
|
+
runGit(cloneDir, ['status', '--porcelain']),
|
|
153
|
+
/^\?\? some-local-cruft\.cjs$/,
|
|
154
|
+
'precondition: clone must show ONLY an untracked file, nothing modified',
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
syncMarketplaceCheckout(cloneDir);
|
|
158
|
+
|
|
159
|
+
assert.equal(
|
|
160
|
+
runGit(cloneDir, ['rev-parse', 'HEAD']),
|
|
161
|
+
originHead,
|
|
162
|
+
'a clone with only an untracked file must still advance to origin — untracked cruft is not a local edit',
|
|
163
|
+
);
|
|
164
|
+
assert.equal(
|
|
165
|
+
existsSync(join(cloneDir, 'some-local-cruft.cjs')),
|
|
166
|
+
true,
|
|
167
|
+
'the untracked file itself must survive the sync — reset --hard never touches it',
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Genuine dirt — a MODIFIED tracked file — must still block the sync.
|
|
171
|
+
writeFileSync(join(originDir, 'file.txt'), 'v3\n');
|
|
172
|
+
runGit(originDir, ['add', '.']);
|
|
173
|
+
runGit(originDir, ['commit', '-m', 'v3']);
|
|
174
|
+
const originHeadV3 = runGit(originDir, ['rev-parse', 'HEAD']);
|
|
175
|
+
writeFileSync(join(cloneDir, 'file.txt'), 'local edit, never committed\n');
|
|
176
|
+
const beforeDirtySync = runGit(cloneDir, ['rev-parse', 'HEAD']);
|
|
177
|
+
|
|
178
|
+
syncMarketplaceCheckout(cloneDir);
|
|
179
|
+
|
|
180
|
+
assert.equal(
|
|
181
|
+
runGit(cloneDir, ['rev-parse', 'HEAD']),
|
|
182
|
+
beforeDirtySync,
|
|
183
|
+
'a real local edit to a TRACKED file must still block the sync (PRESERVE-DIRTY)',
|
|
184
|
+
);
|
|
185
|
+
assert.notEqual(beforeDirtySync, originHeadV3, 'sanity: origin did advance further in this step');
|
|
186
|
+
} finally {
|
|
187
|
+
rmSync(gitTestRoot, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
|
|
100
190
|
const packagedRoot = mkdtempSync(join(tmpdir(), 'rdc-packaged-truth-'));
|
|
101
191
|
try {
|
|
102
192
|
writeFileSync(join(packagedRoot, 'package.json'), JSON.stringify({ version: '9.9.9' }));
|