Skip to content
Open
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
19 changes: 19 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "maister-plugins",
"interface": {
"displayName": "Maister"
},
"plugins": [
{
"name": "maister-codex",
"source": {
"source": "local",
"path": "./plugins/maister-codex"
},
"policy": {
"installation": "AVAILABLE"
},
"category": "Development"
}
]
}
29 changes: 29 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Maister repository guidance

This repository contains workflow plugins. Treat `plugins/maister/` as the Claude Code source and `plugins/maister-copilot/` as generated output; never edit the generated Copilot package directly.

The Codex port lives in `plugins/maister-codex/`. Keep it Codex-native: use `AGENTS.md` for durable project instructions, skills for reusable workflows, and `.codex/agents/*.toml` for project-scoped specialist subagents. Do not add Claude-only tool directives such as `AskUserQuestion`, `TaskCreate`, or `TaskUpdate` to the Codex port.

## Versioning

Releases keep all manifests at the same version (see `CLAUDE.md` "Beta Branch Management" for the branch workflow). Four manifests carry the version:

- `.claude-plugin/marketplace.json`
- `.agents/plugins/marketplace.json` (Codex-native catalog; lists `maister-codex` only, no version field)
- `plugins/maister/.claude-plugin/plugin.json`
- `plugins/maister-copilot/.claude-plugin/plugin.json`
- `plugins/maister-codex/.codex-plugin/plugin.json`

## Validation

Run `make validate` (includes `validate-codex`) before handoff. Additionally validate with the local Codex tooling when available (path is machine-specific):

```bash
python3 ~/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py plugins/maister-codex
```

Validate every changed skill with:

```bash
python3 ~/.codex/skills/.system/skill-creator/scripts/quick_validate.py <skill-directory>
```
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ After `git merge --squash`, git doesn't record that beta's commits were merged.

### Manifest files to update

These three files need version/name changes during the merge workflow:
These four files need version/name changes during the merge workflow:
- `.claude-plugin/marketplace.json` — name + version + descriptions
- `plugins/maister/.claude-plugin/plugin.json` — version + description
- `plugins/maister-copilot/.claude-plugin/plugin.json` — version + description
- `plugins/maister-codex/.codex-plugin/plugin.json` — version + description

## Testing Changes

Expand Down
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build validate clean watch
.PHONY: build validate validate-codex clean watch

build:
bash platforms/copilot-cli/build.sh
Expand All @@ -17,6 +17,24 @@ validate:
@echo "Checking no maister: prefixes in copilot variant..."
@! grep -r 'maister:' plugins/maister-copilot/ --include="*.md" 2>/dev/null || (echo "FAIL: maister: prefix found" && exit 1)
@echo "All checks passed"
@$(MAKE) validate-codex

validate-codex:
@echo "Checking codex plugin manifest parses..."
@node -e "JSON.parse(require('fs').readFileSync('plugins/maister-codex/.codex-plugin/plugin.json','utf8'))" || (echo "FAIL: invalid plugin.json" && exit 1)
@echo "Checking codex hooks config parses..."
@node -e "JSON.parse(require('fs').readFileSync('plugins/maister-codex/hooks/hooks.json','utf8'))" || (echo "FAIL: invalid hooks.json" && exit 1)
@echo "Checking codex hook scripts..."
@for f in plugins/maister-codex/hooks/*.mjs; do node --check "$$f" || exit 1; done
@echo "Checking no Claude-only artifacts in codex skills..."
@! grep -rE 'AskUserQuestion|TaskCreate|TaskUpdate|SlashCommand|Skill tool|Task tool|CLAUDE_PLUGIN_ROOT|CLAUDE\.md|/maister:' plugins/maister-codex/skills/ 2>/dev/null || (echo "FAIL: Claude-only artifact found in codex skills" && exit 1)
@echo "Checking codex SKILL.md names match directories..."
@for d in plugins/maister-codex/skills/*/; do n=$$(basename $$d); grep -q "^name: $$n$$" "$$d/SKILL.md" || (echo "FAIL: $$d frontmatter name != $$n" && exit 1); done
@echo "Checking codex agent template names match filenames..."
@for f in plugins/maister-codex/skills/maister-init/assets/agents/*.toml; do b=$$(basename $$f .toml); grep -q "^name = \"$$b\"$$" "$$f" || (echo "FAIL: $$f name != $$b" && exit 1); done
@echo "Checking codex-native marketplace manifest parses..."
@node -e "const m=JSON.parse(require('fs').readFileSync('.agents/plugins/marketplace.json','utf8')); if(!m.plugins.some(p=>p.name==='maister-codex')) throw new Error('maister-codex missing')" || (echo "FAIL: invalid .agents/plugins/marketplace.json" && exit 1)
@echo "Codex checks passed"

clean:
rm -rf plugins/maister-copilot/
Expand Down
38 changes: 38 additions & 0 deletions plugins/maister-codex/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "maister-codex",
"version": "2.2.3",
"description": "Structured, standards-aware development workflows for Codex",
"author": {
"name": "SkillPanel",
"email": "marek@skillpanel.com"
},
"license": "MIT",
"keywords": [
"development",
"standards",
"testing",
"workflow",
"migration",
"performance",
"research"
],
"skills": "./skills/",
"mcpServers": "./.mcp.json",
"interface": {
"displayName": "Maister Codex",
"shortDescription": "Standards-aware software delivery workflows.",
"longDescription": "Plan, implement, migrate, optimize, research, design, and verify software changes with project standards, explicit gates, resumable artifacts, browser checks, and focused Codex subagents.",
"developerName": "SkillPanel",
"category": "Development",
"capabilities": [
"Interactive",
"Write",
"Testing"
],
"defaultPrompt": [
"Use Maister to plan and implement this feature.",
"Use Maister to migrate or optimize this system.",
"Initialize Maister standards for this repository."
]
}
}
8 changes: 8 additions & 0 deletions plugins/maister-codex/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
13 changes: 13 additions & 0 deletions plugins/maister-codex/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Maister Codex plugin guidance

This directory is the Codex-native Maister plugin. Preserve the workflow principles from `plugins/maister/`: standards awareness, explicit phase gates, user-confirmed rollback, focused verification, and resumable task artifacts. Do not add Claude-only tool directives or treat `plugins/maister-copilot/` as a source.

Use skills for reusable workflows and compose them by loading the named skill and following its instructions. Use Codex subagents only when the user, repository guidance, or the active skill explicitly requests delegation. Keep durable project guidance in `AGENTS.md`; keep workflow state under `.maister/tasks/`.

At a required user decision gate, prefer `request_user_input` when available. Otherwise ask the equivalent concise question in the final response and pause. Use execution approval only for sensitive tool actions, never as a substitute for a product, scope, design, or workflow choice. Never infer approval for rollback, destructive recovery, scope expansion, or acceptance of critical findings.

Codex custom agents are project-scoped, not plugin components. Maintain their distributable templates under `skills/maister-init/assets/agents/`; `$maister-codex:maister-init` copies selected templates into a project's `.codex/agents/` without replacing existing files unless the user approves it.

Keep plugin lifecycle hooks in `hooks/hooks.json`, which Codex discovers by default. Hook commands are inlined as `node -e` one-liners because plugin-root path variables are not reliably expanded across harnesses; the `hooks/*.mjs` files are the readable sources and must be kept in sync with the inline commands. Keep bundled MCP configuration in `.mcp.json` and declare it through `mcpServers` in `.codex-plugin/plugin.json`. The current repository validator does not accept a manifest `hooks` field, so do not add one while the default hook path is used.

Keep skills focused and self-contained. Put detailed reusable guidance in `references/`, deterministic helpers in `scripts/`, and templates in `assets/`; do not create a `README.md` inside a skill folder. Validate every changed skill and the plugin before handoff.
42 changes: 42 additions & 0 deletions plugins/maister-codex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Maister for Codex

Maister provides Codex-native, standards-aware workflows for software delivery. It preserves resumable task artifacts, explicit approval gates, test-first implementation, focused verification, and user-confirmed rollback while leaving the Claude Code source package untouched.

## Workflows

- `$maister-codex:maister-work` classifies and routes software tasks, including resumes from existing `.maister/tasks/` directories.
- `$maister-codex:maister-development`, `$maister-codex:maister-performance`, `$maister-codex:maister-migration`, `$maister-codex:maister-research`, and `$maister-codex:maister-product-design` run full resumable workflows.
- `$maister-codex:maister-quick-dev`, `$maister-codex:maister-quick-plan`, and `$maister-codex:maister-quick-bugfix` handle focused work.
- `$maister-codex:maister-codebase-analysis`, `$maister-codex:maister-implementation-plan-executor`, `$maister-codex:maister-verify`, and `$maister-codex:maister-mockup-studio` provide reusable workflow stages.
- `$maister-codex:maister-reviews-code`, `$maister-codex:maister-reviews-pragmatic`, `$maister-codex:maister-reviews-production-readiness`, `$maister-codex:maister-reviews-reality-check`, and `$maister-codex:maister-reviews-spec-audit` provide focused read-only reviews.
- `$maister-codex:maister-init`, `$maister-codex:maister-docs-manager`, `$maister-codex:maister-standards-discover`, and `$maister-codex:maister-standards-update` initialize and maintain project guidance.

The plugin bundles Playwright MCP for browser verification, mockups, and screenshot-backed documentation. It also includes Node-based lifecycle hooks: reminders for explicit skill invocation and state recovery after compaction, plus a PreToolUse guard that denies destructive shell commands (`git stash`, `git reset --hard`, `git clean`, force-push, `rm -rf`) from non-whitelisted subagents so parallel implementers cannot clobber each other's work. Codex asks you to review and trust plugin hooks before they run. Node.js is required for hooks, the HTML mockup companion, and Playwright MCP; no separate `jq` or Bash dependency is used.

## Specialist agents

`$maister-codex:maister-init` can copy 25 optional specialist templates into the current repository's `.codex/agents/` directory. This is the Codex-native distribution path for project-scoped custom agents; installing the plugin alone does not modify a repository's agent configuration. Existing templates are preserved unless you explicitly approve replacement. Maister workflows delegate to these agents by name when they are installed (for example `maister-task-group-implementer` during implementation waves, `maister-code-reviewer` during verification) and fall back to inline execution when they are absent.

## Installation

Install directly from GitHub:

```bash
codex plugin marketplace add SkillPanel/Maister
codex plugin add maister-codex@maister-plugins
```

Add `--ref <branch>` to install from a branch instead of the default one.

## Local testing

Register a local checkout as a marketplace, then install the plugin:

```bash
codex plugin marketplace add /absolute/path/to/Maister
codex plugin add maister-codex@maister-plugins
```

Codex reads the native catalog at `.agents/plugins/marketplace.json` (named `maister-plugins`), which lists only the Codex package; `.claude-plugin/marketplace.json` remains the Claude Code / Copilot catalog. Start a new Codex thread after installation, then invoke a skill with `$maister-codex:maister-init` or `$maister-codex:maister-work`.

Use `/hooks` to review bundled hooks and `/mcp verbose` to confirm the Playwright server. The first Playwright use may require `npx` to download `@playwright/mcp` under the active sandbox and network policy.
53 changes: 53 additions & 0 deletions plugins/maister-codex/hooks/block-destructive-commands.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Source of the inline `node -e` command in hooks.json (kept path-free there because
// plugin-root variables are not reliably expanded). Keep both in sync when editing.
// Block destructive shell commands from non-implementation subagents.
// Whitelist approach: only explicitly trusted execution agents bypass the check,
// so new agents are protected by default. The main agent (no agent identifier)
// passes through — the user's approval system governs it.

let input = '';
for await (const chunk of process.stdin) input += chunk;

let event = {};
try {
event = input.trim() ? JSON.parse(input) : {};
} catch {
event = {};
}

const agentType =
event.agent_type || event.agentType || event.subagent_type || event.agent || '';
const command =
(event.tool_input && (event.tool_input.command || event.tool_input.cmd)) ||
(event.toolInput && (event.toolInput.command || event.toolInput.cmd)) ||
'';

// Main agent: allow (execution approval handles sensitive actions there).
if (!agentType) process.exit(0);

// Agents that legitimately need full shell access (test execution, docs capture).
// maister-task-group-implementer is intentionally NOT whitelisted: destructive git
// commands are blocked so one implementer cannot clobber parallel siblings.
const trusted = new Set([
'maister-test-suite-runner',
'maister-e2e-test-verifier',
'maister-user-docs-generator',
'maister-docs-operator',
]);
if (trusted.has(String(agentType))) process.exit(0);

const destructive =
/git\s+stash|git\s+reset\s+--hard|git\s+checkout\s+--\s+\.|git\s+checkout\s+\.\s*$|git\s+clean|git\s+push\s+(-f|--force)|rm\s+-rf/i;

if (typeof command === 'string' && destructive.test(command)) {
const payload = {
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: `Destructive command blocked for agent '${agentType}': ${String(command).slice(0, 80)}`,
},
};
process.stdout.write(`${JSON.stringify(payload)}\n`);
}

process.exit(0);
52 changes: 52 additions & 0 deletions plugins/maister-codex/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"description": "Maister workflow reminders, subagent safety guidance, and destructive-command protection. Commands are inlined (node -e) so no plugin-root path variable is required; the sibling .mjs files are the readable sources — keep them in sync.",
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "node -e 'const f=require(\"fs\"),p=require(\"path\");let i=\"\";try{i=f.readFileSync(0,\"utf8\")}catch{}let e={};try{e=i.trim()?JSON.parse(i):{}}catch{}const w=p.resolve(typeof e.cwd===\"string\"&&e.cwd?e.cwd:process.cwd());if(f.existsSync(p.join(w,\".maister\",\"tasks\")))process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:\"SessionStart\",additionalContext:\"MAISTER RESUME CHECK: A .maister/tasks directory exists. If this chat was running a Maister orchestrator before compaction, read the active task orchestrator-state.yml and completed artifacts before continuing. Treat the state file, not recalled chat history, as the resume source of truth. Honor every remaining explicit phase approval gate.\"}})+\"\\n\")'",
"timeout": 10,
"additionalContextLimit": 1000
}
]
},
{
"hooks": [
{
"type": "command",
"command": "node -e 'process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:\"SessionStart\",additionalContext:\"MAISTER PLUGIN RULES: When the user explicitly invokes a $maister-codex:maister-* skill, read that skill completely and follow it before substituting another workflow. Skills compose natively: load each named supporting skill and follow its instructions. For a required phase gate, stop after presenting the review summary and request the specified user decision; do not mark the phase complete until the user responds. Never reset, discard, or roll back work without explicit user approval.\"}})+\"\\n\")'",
"timeout": 10,
"additionalContextLimit": 1200
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash|shell|local_shell",
"hooks": [
{
"type": "command",
"command": "node -e 'const f=require(\"fs\");let i=\"\";try{i=f.readFileSync(0,\"utf8\")}catch{}let e={};try{e=i.trim()?JSON.parse(i):{}}catch{}const a=e.agent_type||e.agentType||e.subagent_type||e.agent||\"\";const t=e.tool_input||e.toolInput||{};const c=t.command||t.cmd||\"\";if(!a)process.exit(0);if([\"maister-test-suite-runner\",\"maister-e2e-test-verifier\",\"maister-user-docs-generator\",\"maister-docs-operator\"].includes(String(a)))process.exit(0);const d=/git\\s+stash|git\\s+reset\\s+--hard|git\\s+checkout\\s+--\\s+\\.|git\\s+checkout\\s+\\.\\s*$|git\\s+clean|git\\s+push\\s+(-f|--force)|rm\\s+-rf/i;if(typeof c===\"string\"&&d.test(c))process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:\"PreToolUse\",permissionDecision:\"deny\",permissionDecisionReason:\"Destructive command blocked for agent \"+a+\": \"+String(c).slice(0,80)}})+\"\\n\")'",
"timeout": 10
}
]
}
],
"SubagentStart": [
{
"hooks": [
{
"type": "command",
"command": "node -e 'process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:\"SubagentStart\",additionalContext:\"MAISTER SUBAGENT SAFETY: Stay within the assigned role, files, and output contract. Preserve unrelated work. Do not run git stash, git reset --hard, git clean, force-push, broad recursive deletion, or any rollback/discard operation. Return concrete evidence and unresolved blockers to the parent agent.\"}})+\"\\n\")'",
"timeout": 10,
"additionalContextLimit": 800
}
]
}
]
}
}
25 changes: 25 additions & 0 deletions plugins/maister-codex/hooks/post-compact-reminder.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Source of the inline `node -e` command in hooks.json (kept path-free there because
// plugin-root variables are not reliably expanded). Keep both in sync when editing.
import fs from 'node:fs';
import path from 'node:path';

let input = '';
for await (const chunk of process.stdin) input += chunk;

let event = {};
try {
event = input.trim() ? JSON.parse(input) : {};
} catch {
event = {};
}

const workspace = path.resolve(typeof event.cwd === 'string' && event.cwd ? event.cwd : process.cwd());
if (fs.existsSync(path.join(workspace, '.maister', 'tasks'))) {
const payload = {
hookSpecificOutput: {
hookEventName: 'SessionStart',
additionalContext: 'MAISTER RESUME CHECK: A .maister/tasks directory exists. If this chat was running a Maister orchestrator before compaction, read the active task orchestrator-state.yml and completed artifacts before continuing. Treat the state file, not recalled chat history, as the resume source of truth. Honor every remaining explicit phase approval gate.',
},
};
process.stdout.write(`${JSON.stringify(payload)}\n`);
}
10 changes: 10 additions & 0 deletions plugins/maister-codex/hooks/subagent-safety-reminder.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Source of the inline `node -e` command in hooks.json (kept path-free there because
// plugin-root variables are not reliably expanded). Keep both in sync when editing.
const payload = {
hookSpecificOutput: {
hookEventName: 'SubagentStart',
additionalContext: 'MAISTER SUBAGENT SAFETY: Stay within the assigned role, files, and output contract. Preserve unrelated work. Do not run git stash, git reset --hard, git clean, force-push, broad recursive deletion, or any rollback/discard operation. Return concrete evidence and unresolved blockers to the parent agent.',
},
};

process.stdout.write(`${JSON.stringify(payload)}\n`);
Loading