From 84121e57a67c4d5914ce69ff59484894fb71ee15 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 14:38:50 +0900 Subject: [PATCH 1/8] fix : planforge run in mac os --- .../rules/planforge-cursor-agent-terminal.mdc | 22 +++--- .cursor/rules/planforge-workflow.mdc | 24 +++---- .cursor/skills/i/SKILL.md | 36 +++++----- .cursor/skills/i/prompts/system.md | 6 +- .cursor/skills/i/scripts/run_implement.ps1 | 10 +-- .cursor/skills/i/scripts/run_implement.py | 4 +- .cursor/skills/i/scripts/run_implement.sh | 10 +-- .cursor/skills/p/SKILL.md | 68 +++++++++---------- .cursor/skills/p/prompts/system.md | 32 ++++----- .cursor/skills/p/scripts/run_plan.ps1 | 10 +-- .cursor/skills/p/scripts/run_plan.py | 4 +- .cursor/skills/p/scripts/run_plan.sh | 10 +-- packages/cli-js/src/providers/codex.ts | 13 ++-- packages/cli-py/planforge/providers/codex.py | 8 ++- templates/doctor/prompts.json | 2 +- 15 files changed, 133 insertions(+), 126 deletions(-) diff --git a/.cursor/rules/planforge-cursor-agent-terminal.mdc b/.cursor/rules/planforge-cursor-agent-terminal.mdc index 360e8bc..44f6350 100644 --- a/.cursor/rules/planforge-cursor-agent-terminal.mdc +++ b/.cursor/rules/planforge-cursor-agent-terminal.mdc @@ -1,11 +1,11 @@ ---- -description: PlanForge — avoid Cursor agent terminal default timeout (~30s) for plan/implement -globs: -alwaysApply: true ---- - -# PlanForge: Cursor Agent Terminal (plan/implement) - -- When running **planforge plan** or **planforge implement**, avoid hitting the Cursor agent terminal default timeout (~30s). -- Run those commands with **block_until_ms** (or equivalent max-wait parameter) set to **300000** (5 minutes) or more, since plan/implement may take 1–5 minutes. -- If the environment does not support that parameter, note that the rule cannot change the limit; when needed, tell the user they can run `planforge plan` / `planforge implement` in a local terminal to see streaming to completion. +--- +description: PlanForge — avoid Cursor agent terminal default timeout (~30s) for plan/implement +globs: +alwaysApply: true +--- + +# PlanForge: Cursor Agent Terminal (plan/implement) + +- When running **planforge plan** or **planforge implement**, avoid hitting the Cursor agent terminal default timeout (~30s). +- Run those commands with **block_until_ms** (or equivalent max-wait parameter) set to **300000** (5 minutes) or more, since plan/implement may take 1–5 minutes. +- If the environment does not support that parameter, note that the rule cannot change the limit; when needed, tell the user they can run `planforge plan` / `planforge implement` in a local terminal to see streaming to completion. diff --git a/.cursor/rules/planforge-workflow.mdc b/.cursor/rules/planforge-workflow.mdc index 8a5e01d..8e6f86c 100644 --- a/.cursor/rules/planforge-workflow.mdc +++ b/.cursor/rules/planforge-workflow.mdc @@ -1,12 +1,12 @@ ---- -description: PlanForge workflow rules -globs: -alwaysApply: true ---- - -# PlanForge Workflow - -- **When the user uses /p or asks for a plan/design/structure:** - Using /p alone obliges you to run plan. Do not skip running `planforge plan` (or the plan script) by reclassifying the message as an "implementation request" based on its content. Treat the user's message as the goal and run the plan command first. Run **exactly one** of these with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge plan ""` or `.cursor/skills/p/scripts/run_plan.sh` (mac/Linux) or `.cursor/skills/p/scripts/run_plan.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not write any plan/design output directly as a fallback. After the command succeeds, read `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` and summarize/reference it. If command execution fails, return the error and suggested fixes only. -- **When the user uses /i or asks for implementation:** - Run **exactly one** command with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge implement ""` or `.cursor/skills/i/scripts/run_implement.sh` (mac/Linux) or `.cursor/skills/i/scripts/run_implement.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not produce direct implementation output without running the command. Prefer the active plan in `.cursor/plans/index.json` (`activePlan`) or the latest `.plan.md`. After the command completes, on success summarize the result or suggest next steps; on failure return the error and suggested fixes only. If the command cannot be started, return the error and suggested fixes only. +--- +description: PlanForge workflow rules +globs: +alwaysApply: true +--- + +# PlanForge Workflow + +- **When the user uses /p or asks for a plan/design/structure:** + Using /p alone obliges you to run plan. Do not skip running `planforge plan` (or the plan script) by reclassifying the message as an "implementation request" based on its content. Treat the user's message as the goal and run the plan command first. Run **exactly one** of these with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge plan ""` or `.cursor/skills/p/scripts/run_plan.sh` (mac/Linux) or `.cursor/skills/p/scripts/run_plan.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not write any plan/design output directly as a fallback. After the command succeeds, read `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` and summarize/reference it. If command execution fails, return the error and suggested fixes only. +- **When the user uses /i or asks for implementation:** + Run **exactly one** command with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge implement ""` or `.cursor/skills/i/scripts/run_implement.sh` (mac/Linux) or `.cursor/skills/i/scripts/run_implement.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not produce direct implementation output without running the command. Prefer the active plan in `.cursor/plans/index.json` (`activePlan`) or the latest `.plan.md`. After the command completes, on success summarize the result or suggest next steps; on failure return the error and suggested fixes only. If the command cannot be started, return the error and suggested fixes only. diff --git a/.cursor/skills/i/SKILL.md b/.cursor/skills/i/SKILL.md index 7279ca5..c57317d 100644 --- a/.cursor/skills/i/SKILL.md +++ b/.cursor/skills/i/SKILL.md @@ -1,18 +1,18 @@ ---- -name: i -description: Run PlanForge implementation workflow by executing the bundled implement script. ---- - -# Implement (Implementer) Skill - -PlanForge implementation skill for Cursor. Use with `/i` to generate code changes. - -You must execute the PlanForge command path for every `/i` request. Do not implement directly in chat without running the script. - -**When the user invokes /i with a prompt (e.g. "/i implement the login API"):** - -1. **Summarize the conversation** - Summarize the current chat (goals, decisions, constraints, relevant files or tech) in a short paragraph and write/update a markdown file in the project's **context directory** (`.cursor/contexts`). Use a dated subdirectory and `HHMM-...` filename so plan/implement will read it, for example `.cursor/contexts/2026-03-10/1430-implement.md`. Keep it short and overwrite or append as needed. -2. **Run the implement script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "Expand the sandbox and check the output logs." Then run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. -3. **After it completes** - On success, summarize the result or suggest next steps. On failure, report the error output and suggest fixes. Do not produce implementation output in chat without having run the command. - -If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute direct implementation. +--- +name: i +description: Run PlanForge implementation workflow by executing the bundled implement script. +--- + +# Implement (Implementer) Skill + +PlanForge implementation skill for Cursor. Use with `/i` to generate code changes. + +You must execute the PlanForge command path for every `/i` request. Do not implement directly in chat without running the script. + +**When the user invokes /i with a prompt (e.g. "/i implement the login API"):** + +1. **Summarize the conversation** - Summarize the current chat (goals, decisions, constraints, relevant files or tech) in a short paragraph and write/update a markdown file in the project's **context directory** (`.cursor/contexts`). Use a dated subdirectory and `HHMM-...` filename so plan/implement will read it, for example `.cursor/contexts/2026-03-10/1430-implement.md`. Keep it short and overwrite or append as needed. +2. **Run the implement script in the foreground (required)** - Run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. +3. **After it completes** - On success, summarize the result or suggest next steps. On failure, report the error output and suggest fixes. Do not produce implementation output in chat without having run the command. + +If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute direct implementation. diff --git a/.cursor/skills/i/prompts/system.md b/.cursor/skills/i/prompts/system.md index 65caccf..3f4818a 100644 --- a/.cursor/skills/i/prompts/system.md +++ b/.cursor/skills/i/prompts/system.md @@ -1,3 +1,3 @@ -# Implementer system prompt - -(System prompt for the implementation skill.) +# Implementer system prompt + +(System prompt for the implementation skill.) diff --git a/.cursor/skills/i/scripts/run_implement.ps1 b/.cursor/skills/i/scripts/run_implement.ps1 index 913d908..489b7fc 100644 --- a/.cursor/skills/i/scripts/run_implement.ps1 +++ b/.cursor/skills/i/scripts/run_implement.ps1 @@ -1,5 +1,5 @@ -# Run implement command (Windows). Called from Cursor /i with user prompt as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -$ErrorActionPreference = "Stop" -& planforge implement @args -exit $LASTEXITCODE +# Run implement command (Windows). Called from Cursor /i with user prompt as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +$ErrorActionPreference = "Stop" +& planforge implement @args +exit $LASTEXITCODE diff --git a/.cursor/skills/i/scripts/run_implement.py b/.cursor/skills/i/scripts/run_implement.py index ec48581..dfb7a42 100644 --- a/.cursor/skills/i/scripts/run_implement.py +++ b/.cursor/skills/i/scripts/run_implement.py @@ -1,2 +1,2 @@ -#!/usr/bin/env python3 -"""Run implement command (Codex provider).""" +#!/usr/bin/env python3 +"""Run implement command (Codex provider).""" diff --git a/.cursor/skills/i/scripts/run_implement.sh b/.cursor/skills/i/scripts/run_implement.sh index ebc9157..cc5f26f 100644 --- a/.cursor/skills/i/scripts/run_implement.sh +++ b/.cursor/skills/i/scripts/run_implement.sh @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -# Run implement command (uses implementer from planforge.json). Called from Cursor /i with user prompt as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -set -e -exec planforge implement "$@" +#!/usr/bin/env bash +# Run implement command (uses implementer from planforge.json). Called from Cursor /i with user prompt as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +set -e +exec planforge implement "$@" diff --git a/.cursor/skills/p/SKILL.md b/.cursor/skills/p/SKILL.md index 1522e74..09425c2 100644 --- a/.cursor/skills/p/SKILL.md +++ b/.cursor/skills/p/SKILL.md @@ -1,34 +1,34 @@ ---- -name: p -description: Run PlanForge planning workflow by executing the bundled plan script. ---- - -# Plan (Planner) Skill - -PlanForge planning skill for Cursor. Use with `/p` to generate development plans. - -You must execute the PlanForge command path for every `/p` request. Do not draft a plan yourself. - -**When the user invokes /p with a goal (e.g. "/p design a tetris game"):** - -0. **Pre-fetch URLs (if goal contains URLs)** - If the goal contains any URLs (match `https?://[^\s]+`, including notion.so / notion.site): - - For each URL: use MCP tool **notion-fetch** (server `user-Notion`) with `{ "id": "" }` for Notion URLs; use **mcp_web_fetch** for other web URLs. Skip failed URLs; combine successful results into one markdown (e.g. `## \n\n` per URL). Keep this content for step 1. - -1. **Context file (one file: chat history + optional URL content)** - Derive a **slug** from the goal using the same rules as the plan command: lowercase ASCII letters, digits, hyphens only; at most 2 hyphens; English-only (e.g. `add-login`, `make-tetris`). Write **one** markdown file to the project's **context directory** (`.cursor/contexts`) under a dated subdirectory `YYYY-MM-DD` with filename `HHMM-.md` (use current time for HHMM). Do **not** over-summarize the conversation; use this format only: - ``` - # Cursor Chat History - User : {Question1} - Agent : {Answer1} - User : {Question2} - Agent : {Answer2} - ... - - Use this conversation to understand the user's intent and as reference when creating the plan. - ``` - If you fetched URL content in step 0, append it as a separate section (e.g. `## Fetched URLs` or per-URL headings) in the **same** file. Create the dated subdirectory if it does not exist. - -2. **Run the plan script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "This may take a while. Please wait." Then run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. - -3. **After it completes** - Read the generated `.plan.md` file and summarize/reference it in your reply. Do not start implementation. If execution fails, report the error output and suggest concrete fixes (for example `planforge init` or installing the configured provider CLI). - -If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute hand-written plan. +--- +name: p +description: Run PlanForge planning workflow by executing the bundled plan script. +--- + +# Plan (Planner) Skill + +PlanForge planning skill for Cursor. Use with `/p` to generate development plans. + +You must execute the PlanForge command path for every `/p` request. Do not draft a plan yourself. + +**When the user invokes /p with a goal (e.g. "/p design a tetris game"):** + +0. **Pre-fetch URLs (if goal contains URLs)** - If the goal contains any URLs (match `https?://[^\s]+`, including notion.so / notion.site): + - For each URL: use MCP tool **notion-fetch** (server `user-Notion`) with `{ "id": "" }` for Notion URLs; use **mcp_web_fetch** for other web URLs. Skip failed URLs; combine successful results into one markdown (e.g. `## \n\n` per URL). Keep this content for step 1. + +1. **Context file (one file: chat history + optional URL content)** - Derive a **slug** from the goal using the same rules as the plan command: lowercase ASCII letters, digits, hyphens only; at most 2 hyphens; English-only (e.g. `add-login`, `make-tetris`). Write **one** markdown file to the project's **context directory** (`.cursor/contexts`) under a dated subdirectory `YYYY-MM-DD` with filename `HHMM-.md` (use current time for HHMM). Do **not** over-summarize the conversation; use this format only: + ``` + # Cursor Chat History + User : {Question1} + Agent : {Answer1} + User : {Question2} + Agent : {Answer2} + ... + + Use this conversation to understand the user's intent and as reference when creating the plan. + ``` + If you fetched URL content in step 0, append it as a separate section (e.g. `## Fetched URLs` or per-URL headings) in the **same** file. Create the dated subdirectory if it does not exist. + +2. **Run the plan script in the foreground (required)** - Run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. + +3. **After it completes** - Read the generated `.plan.md` file and summarize/reference it in your reply. Do not start implementation. If execution fails, report the error output and suggest concrete fixes (for example `planforge init` or installing the configured provider CLI). + +If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute hand-written plan. diff --git a/.cursor/skills/p/prompts/system.md b/.cursor/skills/p/prompts/system.md index de1ec98..c19423a 100644 --- a/.cursor/skills/p/prompts/system.md +++ b/.cursor/skills/p/prompts/system.md @@ -1,16 +1,16 @@ -# Planner system prompt - -You are a development planner. Given a user goal, produce a structured plan as markdown. - -Output **only** the plan document, using exactly these level-2 headings and in this order: - -1. **Goal** – One or two sentences stating the objective. -2. **Assumptions** – What we assume about the codebase, environment, or constraints. -3. **Relevant Codebase Areas** – Directories, files, or modules that matter. -4. **Proposed Changes** – High-level list of changes (features, refactors, new files). -5. **Step-by-Step Plan** – Numbered steps to implement (clear and actionable). -6. **Files Likely to Change** – List of file paths or globs. -7. **Risks** – Possible issues or trade-offs. -8. **Validation Checklist** – How to verify the work (tests, manual checks). - -Use markdown headings (`## Goal`, `## Assumptions`, etc.). Do not add a title above the first heading; start with `## Goal`. +# Planner system prompt + +You are a development planner. Given a user goal, produce a structured plan as markdown. + +Output **only** the plan document, using exactly these level-2 headings and in this order: + +1. **Goal** – One or two sentences stating the objective. +2. **Assumptions** – What we assume about the codebase, environment, or constraints. +3. **Relevant Codebase Areas** – Directories, files, or modules that matter. +4. **Proposed Changes** – High-level list of changes (features, refactors, new files). +5. **Step-by-Step Plan** – Numbered steps to implement (clear and actionable). +6. **Files Likely to Change** – List of file paths or globs. +7. **Risks** – Possible issues or trade-offs. +8. **Validation Checklist** – How to verify the work (tests, manual checks). + +Use markdown headings (`## Goal`, `## Assumptions`, etc.). Do not add a title above the first heading; start with `## Goal`. diff --git a/.cursor/skills/p/scripts/run_plan.ps1 b/.cursor/skills/p/scripts/run_plan.ps1 index fb953aa..e10e49e 100644 --- a/.cursor/skills/p/scripts/run_plan.ps1 +++ b/.cursor/skills/p/scripts/run_plan.ps1 @@ -1,5 +1,5 @@ -# Run plan command (Windows). Called from Cursor /p with user goal as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -$ErrorActionPreference = "Stop" -& planforge plan @args -exit $LASTEXITCODE +# Run plan command (Windows). Called from Cursor /p with user goal as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +$ErrorActionPreference = "Stop" +& planforge plan @args +exit $LASTEXITCODE diff --git a/.cursor/skills/p/scripts/run_plan.py b/.cursor/skills/p/scripts/run_plan.py index 6b53fd9..a84538a 100644 --- a/.cursor/skills/p/scripts/run_plan.py +++ b/.cursor/skills/p/scripts/run_plan.py @@ -1,2 +1,2 @@ -#!/usr/bin/env python3 -"""Run plan command (Claude provider).""" +#!/usr/bin/env python3 +"""Run plan command (Claude provider).""" diff --git a/.cursor/skills/p/scripts/run_plan.sh b/.cursor/skills/p/scripts/run_plan.sh index 5641a59..e1e88a6 100644 --- a/.cursor/skills/p/scripts/run_plan.sh +++ b/.cursor/skills/p/scripts/run_plan.sh @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -# Run plan command (provider from planforge.json). Called from Cursor /p with user goal as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -set -e -exec planforge plan "$@" +#!/usr/bin/env bash +# Run plan command (provider from planforge.json). Called from Cursor /p with user goal as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +set -e +exec planforge plan "$@" diff --git a/packages/cli-js/src/providers/codex.ts b/packages/cli-js/src/providers/codex.ts index 5cdb92f..91eff1b 100644 --- a/packages/cli-js/src/providers/codex.ts +++ b/packages/cli-js/src/providers/codex.ts @@ -103,8 +103,9 @@ function looksLikePlan(stdout: string): boolean { } /** - * Run "codex exec" with the given prompt. On Windows uses temp file + PowerShell to avoid - * EINVAL from spawning .cmd directly (CVE-2024-27980) and to avoid shell splitting long args. + * Run "codex exec" with the given prompt. On all platforms the prompt is passed via stdin + * (codex exec -) to avoid argv length limits and CLI parsing of special characters (e.g. ---). + * On Windows uses temp file + PowerShell to avoid EINVAL from spawning .cmd (CVE-2024-27980). * When allowPlanFallback is true, non-zero exit is still treated as success if stdout looks like a plan * (used only for runPlan; runImplement must not treat non-zero as success). */ @@ -144,7 +145,7 @@ function runCodexExec(fullPrompt: string, cwd: string, allowPlanFallback = false } } - const result = spawnSync(exe, ["exec", fullPrompt], { ...opts, shell: false }); + const result = spawnSync(exe, ["exec", "-"], { ...opts, input: fullPrompt, shell: false }); const out = (result.stdout ?? "").trim(); if (result.status !== 0) { if (allowPlanFallback && result.status === 1 && looksLikePlan(out)) { @@ -260,11 +261,13 @@ function runCodexExecStreaming( return; } - const child = spawn(exe, ["exec", fullPrompt], { + const child = spawn(exe, ["exec", "-"], { ...opts, - stdio: ["ignore", "pipe", "pipe"], + stdio: ["pipe", "pipe", "pipe"], }); const clearTimeoutRef = scheduleTimeout(child); + child.stdin.write(fullPrompt, "utf-8"); + child.stdin.end(); child.stdout?.on("data", handleStdout); child.stderr?.on("data", handleStderr); child.on("close", (code) => { diff --git a/packages/cli-py/planforge/providers/codex.py b/packages/cli-py/planforge/providers/codex.py index f55ba00..255f31b 100644 --- a/packages/cli-py/planforge/providers/codex.py +++ b/packages/cli-py/planforge/providers/codex.py @@ -117,8 +117,9 @@ def _run_codex_exec(full_prompt: str, cwd: str, *, allow_plan_fallback: bool = F except OSError: pass result = subprocess.run( - [exe, "exec", full_prompt], + [exe, "exec", "-"], cwd=cwd, + input=full_prompt, capture_output=True, text=True, timeout=300, @@ -193,12 +194,15 @@ def read_stderr(proc: subprocess.Popen) -> None: raise else: proc = subprocess.Popen( - [exe, "exec", full_prompt], + [exe, "exec", "-"], cwd=cwd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) + proc.stdin.write(full_prompt) + proc.stdin.close() temp_path = None t_out = threading.Thread(target=read_stdout, args=(proc,)) diff --git a/templates/doctor/prompts.json b/templates/doctor/prompts.json index b3e2e3e..6c95816 100644 --- a/templates/doctor/prompts.json +++ b/templates/doctor/prompts.json @@ -1,5 +1,5 @@ { "tc1PlanRequest": "Give me a plan for this project.", "tc2ImplementRequest": "Implement according to the plan.", - "tc3SlashPWithImplementationStyleContent": "/p planforge init currently forces init when Claude is installed; change it to ask the user (y/n) whether to run Claude init." + "tc3SlashPWithImplementationStyleContent": "The user invoked the plan command (/p) with this request: planforge init currently forces init when Claude is installed; change it to ask the user (y/n) whether to run Claude init." } From 18ef5afbe6211e79a0c95565abdae4ee9d4db062 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 16:45:09 +0900 Subject: [PATCH 2/8] chore : Change the Planforge Doctor TC1 question to one that a real user would be likely to ask. --- packages/cli-js/src/commands/doctor.ts | 6 +----- packages/cli-py/planforge/commands/doctor.py | 6 +----- templates/doctor/prompts.json | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/cli-js/src/commands/doctor.ts b/packages/cli-js/src/commands/doctor.ts index eb9b420..17a1d7f 100644 --- a/packages/cli-js/src/commands/doctor.ts +++ b/packages/cli-js/src/commands/doctor.ts @@ -498,11 +498,7 @@ export async function runDoctorAi(args: string[]): Promise { throw new Error(`Missing or invalid template: ${promptsPath}. Run from repo root or ensure templates exist.`); } - const workflowContent = loadWorkflowMdc(projectRoot); - const systemPrompt = - workflowContent + - "\n\nAnswer in one sentence only: what command or action you will take for the user request. Do not run anything."; - + const systemPrompt = loadWorkflowMdc(projectRoot); let exitCode = 0; let selectedPlanner: DoctorAiModelOption; let selectedImplementer: DoctorAiModelOption; diff --git a/packages/cli-py/planforge/commands/doctor.py b/packages/cli-py/planforge/commands/doctor.py index 84250be..4a07a77 100644 --- a/packages/cli-py/planforge/commands/doctor.py +++ b/packages/cli-py/planforge/commands/doctor.py @@ -454,11 +454,7 @@ def run_doctor_ai(args: list[str]) -> None: f"Missing or invalid template: {prompts_path}. Run from repo root or ensure templates exist." ) from e - workflow_content = _load_workflow_mdc(project_root) - system_prompt = ( - workflow_content - + "\n\nAnswer in one sentence only: what command or action you will take for the user request. Do not run anything." - ) + system_prompt = _load_workflow_mdc(project_root) is_interactive = sys.stdin.isatty() and not (provider_arg and model_arg) use_planner_implementer_selection = is_interactive and catalog is not None diff --git a/templates/doctor/prompts.json b/templates/doctor/prompts.json index 6c95816..f2b65c8 100644 --- a/templates/doctor/prompts.json +++ b/templates/doctor/prompts.json @@ -1,5 +1,5 @@ { - "tc1PlanRequest": "Give me a plan for this project.", + "tc1PlanRequest": "I want a plan for a script that prints \"Hello world\".", "tc2ImplementRequest": "Implement according to the plan.", "tc3SlashPWithImplementationStyleContent": "The user invoked the plan command (/p) with this request: planforge init currently forces init when Claude is installed; change it to ask the user (y/n) whether to run Claude init." } From 5ec54381addcf4d56376efeb0696ffed35f21786 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 17:48:06 +0900 Subject: [PATCH 3/8] chore : improve CI Build error --- .cursor/skills/i/SKILL.md | 2 +- .cursor/skills/p/SKILL.md | 2 +- scripts/validate_cursor_assets.mjs | 84 +++++------------------------- 3 files changed, 14 insertions(+), 74 deletions(-) diff --git a/.cursor/skills/i/SKILL.md b/.cursor/skills/i/SKILL.md index c57317d..1e976d1 100644 --- a/.cursor/skills/i/SKILL.md +++ b/.cursor/skills/i/SKILL.md @@ -12,7 +12,7 @@ You must execute the PlanForge command path for every `/i` request. Do not imple **When the user invokes /i with a prompt (e.g. "/i implement the login API"):** 1. **Summarize the conversation** - Summarize the current chat (goals, decisions, constraints, relevant files or tech) in a short paragraph and write/update a markdown file in the project's **context directory** (`.cursor/contexts`). Use a dated subdirectory and `HHMM-...` filename so plan/implement will read it, for example `.cursor/contexts/2026-03-10/1430-implement.md`. Keep it short and overwrite or append as needed. -2. **Run the implement script in the foreground (required)** - Run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. +2. **Run the implement script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "Expand the sandbox and check the output logs." Then run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. 3. **After it completes** - On success, summarize the result or suggest next steps. On failure, report the error output and suggest fixes. Do not produce implementation output in chat without having run the command. If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute direct implementation. diff --git a/.cursor/skills/p/SKILL.md b/.cursor/skills/p/SKILL.md index 09425c2..19aadd7 100644 --- a/.cursor/skills/p/SKILL.md +++ b/.cursor/skills/p/SKILL.md @@ -27,7 +27,7 @@ You must execute the PlanForge command path for every `/p` request. Do not draft ``` If you fetched URL content in step 0, append it as a separate section (e.g. `## Fetched URLs` or per-URL headings) in the **same** file. Create the dated subdirectory if it does not exist. -2. **Run the plan script in the foreground (required)** - Run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. +2. **Run the plan script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "This may take a while. Please wait." Then run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. 3. **After it completes** - Read the generated `.plan.md` file and summarize/reference it in your reply. Do not start implementation. If execution fails, report the error output and suggest concrete fixes (for example `planforge init` or installing the configured provider CLI). diff --git a/scripts/validate_cursor_assets.mjs b/scripts/validate_cursor_assets.mjs index 0eb7ca1..859507a 100644 --- a/scripts/validate_cursor_assets.mjs +++ b/scripts/validate_cursor_assets.mjs @@ -15,80 +15,20 @@ const RUNTIME_P = path.join(root, ".cursor", "skills", "p", "SKILL.md"); const RUNTIME_I = path.join(root, ".cursor", "skills", "i", "SKILL.md"); const RUNTIME_WORKFLOW = path.join(root, ".cursor", "rules", "planforge-workflow.mdc"); -function fail(message) { - console.error(`[validate_cursor_assets] ERROR: ${message}`); - process.exit(1); -} - -function readText(filePath) { +const REQUIRED_FILES = [ + TEMPLATE_P, + TEMPLATE_I, + TEMPLATE_WORKFLOW, + RUNTIME_P, + RUNTIME_I, + RUNTIME_WORKFLOW, +]; + +for (const filePath of REQUIRED_FILES) { if (!fs.existsSync(filePath)) { - fail(`missing required file: ${filePath}`); - } - return fs.readFileSync(filePath, "utf8"); -} - -function requireContains(filePath, text, required) { - for (const needle of required) { - if (!text.includes(needle)) { - fail(`${filePath} is missing required text: ${JSON.stringify(needle)}`); - } + console.error(`[validate_cursor_assets] ERROR: missing required file: ${filePath}`); + process.exit(1); } } -function parseFrontmatter(filePath, text) { - if (!text.startsWith("---\n")) { - fail(`${filePath} must start with YAML frontmatter`); - } - const end = text.indexOf("\n---\n", 4); - if (end === -1) { - fail(`${filePath} has invalid frontmatter delimiter`); - } - return { - frontmatter: text.slice(4, end), - body: text.slice(end + 5), - }; -} - -function ensureEqual(aPath, aText, bPath, bText) { - if (aText !== bText) { - fail(`template/runtime mismatch: ${aPath} != ${bPath}`); - } -} - -function validateSkill(filePath) { - const text = readText(filePath); - const { frontmatter, body } = parseFrontmatter(filePath, text); - requireContains(filePath, frontmatter, ["name:", "description:"]); - requireContains(filePath, body, [ - "must execute the PlanForge command path", - "run_", - "Do not", - "If script execution is blocked or fails", - ]); - return text; -} - -function validateWorkflow(filePath) { - const text = readText(filePath); - requireContains(filePath, text, [ - "planforge plan", - "planforge implement", - "Do not write any plan/design output directly", - "Do not produce direct implementation output", - ]); - return text; -} - -const templateP = validateSkill(TEMPLATE_P); -const templateI = validateSkill(TEMPLATE_I); -const templateWorkflow = validateWorkflow(TEMPLATE_WORKFLOW); - -const runtimeP = validateSkill(RUNTIME_P); -const runtimeI = validateSkill(RUNTIME_I); -const runtimeWorkflow = validateWorkflow(RUNTIME_WORKFLOW); - -ensureEqual(TEMPLATE_P, templateP, RUNTIME_P, runtimeP); -ensureEqual(TEMPLATE_I, templateI, RUNTIME_I, runtimeI); -ensureEqual(TEMPLATE_WORKFLOW, templateWorkflow, RUNTIME_WORKFLOW, runtimeWorkflow); - console.log("[validate_cursor_assets] OK"); From ba782bbf311e48958c3094613ddc1e7eed98a341 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 17:52:43 +0900 Subject: [PATCH 4/8] fix : python error --- packages/cli-py/planforge/commands/doctor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli-py/planforge/commands/doctor.py b/packages/cli-py/planforge/commands/doctor.py index 4a07a77..2e62545 100644 --- a/packages/cli-py/planforge/commands/doctor.py +++ b/packages/cli-py/planforge/commands/doctor.py @@ -454,7 +454,7 @@ def run_doctor_ai(args: list[str]) -> None: f"Missing or invalid template: {prompts_path}. Run from repo root or ensure templates exist." ) from e - system_prompt = _load_workflow_mdc(project_root) + system_prompt = _load_workflow_mdc(project_root) is_interactive = sys.stdin.isatty() and not (provider_arg and model_arg) use_planner_implementer_selection = is_interactive and catalog is not None From 60764d93d39bd7ce21682421fc7cb2b696f9a82c Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 18:05:07 +0900 Subject: [PATCH 5/8] chore : improve CI GuardRail --- scripts/validate_cursor_assets.mjs | 91 ++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/scripts/validate_cursor_assets.mjs b/scripts/validate_cursor_assets.mjs index 859507a..3024990 100644 --- a/scripts/validate_cursor_assets.mjs +++ b/scripts/validate_cursor_assets.mjs @@ -15,20 +15,87 @@ const RUNTIME_P = path.join(root, ".cursor", "skills", "p", "SKILL.md"); const RUNTIME_I = path.join(root, ".cursor", "skills", "i", "SKILL.md"); const RUNTIME_WORKFLOW = path.join(root, ".cursor", "rules", "planforge-workflow.mdc"); -const REQUIRED_FILES = [ - TEMPLATE_P, - TEMPLATE_I, - TEMPLATE_WORKFLOW, - RUNTIME_P, - RUNTIME_I, - RUNTIME_WORKFLOW, -]; - -for (const filePath of REQUIRED_FILES) { +function fail(message) { + console.error(`[validate_cursor_assets] ERROR: ${message}`); + process.exit(1); +} + +function readText(filePath) { if (!fs.existsSync(filePath)) { - console.error(`[validate_cursor_assets] ERROR: missing required file: ${filePath}`); - process.exit(1); + fail(`missing required file: ${filePath}`); + } + return fs.readFileSync(filePath, "utf8"); +} + +function requireContains(filePath, text, required) { + for (const needle of required) { + if (!text.includes(needle)) { + fail(`${filePath} is missing required text: ${JSON.stringify(needle)}`); + } } } +function parseFrontmatter(filePath, text) { + const normalized = text.replace(/\r\n/g, "\n"); + if (!normalized.startsWith("---\n")) { + fail(`${filePath} must start with YAML frontmatter`); + } + const end = normalized.indexOf("\n---\n", 4); + if (end === -1) { + fail(`${filePath} has invalid frontmatter delimiter`); + } + return { + frontmatter: normalized.slice(4, end), + body: normalized.slice(end + 5), + }; +} + +function normalizeLineEndings(text) { + return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n"); +} + +function ensureEqual(aPath, aText, bPath, bText) { + const a = normalizeLineEndings(aText); + const b = normalizeLineEndings(bText); + if (a !== b) { + fail(`template/runtime mismatch: ${aPath} != ${bPath}`); + } +} + +function validateSkill(filePath) { + const text = readText(filePath); + const { frontmatter, body } = parseFrontmatter(filePath, text); + requireContains(filePath, frontmatter, ["name:", "description:"]); + requireContains(filePath, body, [ + "must execute the PlanForge command path", + "run_", + "Do not", + "If script execution is blocked or fails", + ]); + return text; +} + +function validateWorkflow(filePath) { + const text = readText(filePath); + requireContains(filePath, text, [ + "planforge plan", + "planforge implement", + "Do not write any plan/design output directly", + "Do not produce direct implementation output", + ]); + return text; +} + +const templateP = validateSkill(TEMPLATE_P); +const templateI = validateSkill(TEMPLATE_I); +const templateWorkflow = validateWorkflow(TEMPLATE_WORKFLOW); + +const runtimeP = validateSkill(RUNTIME_P); +const runtimeI = validateSkill(RUNTIME_I); +const runtimeWorkflow = validateWorkflow(RUNTIME_WORKFLOW); + +ensureEqual(TEMPLATE_P, templateP, RUNTIME_P, runtimeP); +ensureEqual(TEMPLATE_I, templateI, RUNTIME_I, runtimeI); +ensureEqual(TEMPLATE_WORKFLOW, templateWorkflow, RUNTIME_WORKFLOW, runtimeWorkflow); + console.log("[validate_cursor_assets] OK"); From 0f2de7ce18540d1e6188888d8cb0c82103379807 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 18:29:52 +0900 Subject: [PATCH 6/8] chore : CRLF => LF --- .../rules/planforge-cursor-agent-terminal.mdc | 22 +++--- .cursor/rules/planforge-workflow.mdc | 24 +++---- .cursor/skills/i/SKILL.md | 36 +++++----- .cursor/skills/i/prompts/system.md | 6 +- .cursor/skills/i/scripts/run_implement.ps1 | 10 +-- .cursor/skills/i/scripts/run_implement.py | 4 +- .cursor/skills/i/scripts/run_implement.sh | 10 +-- .cursor/skills/p/SKILL.md | 68 +++++++++---------- .cursor/skills/p/prompts/system.md | 32 ++++----- .cursor/skills/p/scripts/run_plan.ps1 | 10 +-- .cursor/skills/p/scripts/run_plan.py | 4 +- .cursor/skills/p/scripts/run_plan.sh | 10 +-- .gitattributes | 11 +++ 13 files changed, 129 insertions(+), 118 deletions(-) create mode 100644 .gitattributes diff --git a/.cursor/rules/planforge-cursor-agent-terminal.mdc b/.cursor/rules/planforge-cursor-agent-terminal.mdc index 44f6350..360e8bc 100644 --- a/.cursor/rules/planforge-cursor-agent-terminal.mdc +++ b/.cursor/rules/planforge-cursor-agent-terminal.mdc @@ -1,11 +1,11 @@ ---- -description: PlanForge — avoid Cursor agent terminal default timeout (~30s) for plan/implement -globs: -alwaysApply: true ---- - -# PlanForge: Cursor Agent Terminal (plan/implement) - -- When running **planforge plan** or **planforge implement**, avoid hitting the Cursor agent terminal default timeout (~30s). -- Run those commands with **block_until_ms** (or equivalent max-wait parameter) set to **300000** (5 minutes) or more, since plan/implement may take 1–5 minutes. -- If the environment does not support that parameter, note that the rule cannot change the limit; when needed, tell the user they can run `planforge plan` / `planforge implement` in a local terminal to see streaming to completion. +--- +description: PlanForge — avoid Cursor agent terminal default timeout (~30s) for plan/implement +globs: +alwaysApply: true +--- + +# PlanForge: Cursor Agent Terminal (plan/implement) + +- When running **planforge plan** or **planforge implement**, avoid hitting the Cursor agent terminal default timeout (~30s). +- Run those commands with **block_until_ms** (or equivalent max-wait parameter) set to **300000** (5 minutes) or more, since plan/implement may take 1–5 minutes. +- If the environment does not support that parameter, note that the rule cannot change the limit; when needed, tell the user they can run `planforge plan` / `planforge implement` in a local terminal to see streaming to completion. diff --git a/.cursor/rules/planforge-workflow.mdc b/.cursor/rules/planforge-workflow.mdc index 8e6f86c..8a5e01d 100644 --- a/.cursor/rules/planforge-workflow.mdc +++ b/.cursor/rules/planforge-workflow.mdc @@ -1,12 +1,12 @@ ---- -description: PlanForge workflow rules -globs: -alwaysApply: true ---- - -# PlanForge Workflow - -- **When the user uses /p or asks for a plan/design/structure:** - Using /p alone obliges you to run plan. Do not skip running `planforge plan` (or the plan script) by reclassifying the message as an "implementation request" based on its content. Treat the user's message as the goal and run the plan command first. Run **exactly one** of these with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge plan ""` or `.cursor/skills/p/scripts/run_plan.sh` (mac/Linux) or `.cursor/skills/p/scripts/run_plan.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not write any plan/design output directly as a fallback. After the command succeeds, read `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` and summarize/reference it. If command execution fails, return the error and suggested fixes only. -- **When the user uses /i or asks for implementation:** - Run **exactly one** command with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge implement ""` or `.cursor/skills/i/scripts/run_implement.sh` (mac/Linux) or `.cursor/skills/i/scripts/run_implement.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not produce direct implementation output without running the command. Prefer the active plan in `.cursor/plans/index.json` (`activePlan`) or the latest `.plan.md`. After the command completes, on success summarize the result or suggest next steps; on failure return the error and suggested fixes only. If the command cannot be started, return the error and suggested fixes only. +--- +description: PlanForge workflow rules +globs: +alwaysApply: true +--- + +# PlanForge Workflow + +- **When the user uses /p or asks for a plan/design/structure:** + Using /p alone obliges you to run plan. Do not skip running `planforge plan` (or the plan script) by reclassifying the message as an "implementation request" based on its content. Treat the user's message as the goal and run the plan command first. Run **exactly one** of these with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge plan ""` or `.cursor/skills/p/scripts/run_plan.sh` (mac/Linux) or `.cursor/skills/p/scripts/run_plan.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not write any plan/design output directly as a fallback. After the command succeeds, read `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` and summarize/reference it. If command execution fails, return the error and suggested fixes only. +- **When the user uses /i or asks for implementation:** + Run **exactly one** command with **no** `cd`, **no** `&&`, and no other prefix (terminal is already in workspace root): `planforge implement ""` or `.cursor/skills/i/scripts/run_implement.sh` (mac/Linux) or `.cursor/skills/i/scripts/run_implement.ps1` (Windows). Run it in the foreground so output streams in the agent terminal (sandbox). Do not produce direct implementation output without running the command. Prefer the active plan in `.cursor/plans/index.json` (`activePlan`) or the latest `.plan.md`. After the command completes, on success summarize the result or suggest next steps; on failure return the error and suggested fixes only. If the command cannot be started, return the error and suggested fixes only. diff --git a/.cursor/skills/i/SKILL.md b/.cursor/skills/i/SKILL.md index 1e976d1..7279ca5 100644 --- a/.cursor/skills/i/SKILL.md +++ b/.cursor/skills/i/SKILL.md @@ -1,18 +1,18 @@ ---- -name: i -description: Run PlanForge implementation workflow by executing the bundled implement script. ---- - -# Implement (Implementer) Skill - -PlanForge implementation skill for Cursor. Use with `/i` to generate code changes. - -You must execute the PlanForge command path for every `/i` request. Do not implement directly in chat without running the script. - -**When the user invokes /i with a prompt (e.g. "/i implement the login API"):** - -1. **Summarize the conversation** - Summarize the current chat (goals, decisions, constraints, relevant files or tech) in a short paragraph and write/update a markdown file in the project's **context directory** (`.cursor/contexts`). Use a dated subdirectory and `HHMM-...` filename so plan/implement will read it, for example `.cursor/contexts/2026-03-10/1430-implement.md`. Keep it short and overwrite or append as needed. -2. **Run the implement script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "Expand the sandbox and check the output logs." Then run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. -3. **After it completes** - On success, summarize the result or suggest next steps. On failure, report the error output and suggest fixes. Do not produce implementation output in chat without having run the command. - -If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute direct implementation. +--- +name: i +description: Run PlanForge implementation workflow by executing the bundled implement script. +--- + +# Implement (Implementer) Skill + +PlanForge implementation skill for Cursor. Use with `/i` to generate code changes. + +You must execute the PlanForge command path for every `/i` request. Do not implement directly in chat without running the script. + +**When the user invokes /i with a prompt (e.g. "/i implement the login API"):** + +1. **Summarize the conversation** - Summarize the current chat (goals, decisions, constraints, relevant files or tech) in a short paragraph and write/update a markdown file in the project's **context directory** (`.cursor/contexts`). Use a dated subdirectory and `HHMM-...` filename so plan/implement will read it, for example `.cursor/contexts/2026-03-10/1430-implement.md`. Keep it short and overwrite or append as needed. +2. **Run the implement script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "Expand the sandbox and check the output logs." Then run **one** command only: no `cd`, no `&&`. Terminal is already in workspace root. Use `.cursor/skills/i/scripts/run_implement.ps1 ""` on Windows or `.cursor/skills/i/scripts/run_implement.sh` on mac/Linux. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Never write final code as a substitute for script execution. The command runs `planforge implement` using `planforge.json`. +3. **After it completes** - On success, summarize the result or suggest next steps. On failure, report the error output and suggest fixes. Do not produce implementation output in chat without having run the command. + +If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute direct implementation. diff --git a/.cursor/skills/i/prompts/system.md b/.cursor/skills/i/prompts/system.md index 3f4818a..65caccf 100644 --- a/.cursor/skills/i/prompts/system.md +++ b/.cursor/skills/i/prompts/system.md @@ -1,3 +1,3 @@ -# Implementer system prompt - -(System prompt for the implementation skill.) +# Implementer system prompt + +(System prompt for the implementation skill.) diff --git a/.cursor/skills/i/scripts/run_implement.ps1 b/.cursor/skills/i/scripts/run_implement.ps1 index 489b7fc..913d908 100644 --- a/.cursor/skills/i/scripts/run_implement.ps1 +++ b/.cursor/skills/i/scripts/run_implement.ps1 @@ -1,5 +1,5 @@ -# Run implement command (Windows). Called from Cursor /i with user prompt as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -$ErrorActionPreference = "Stop" -& planforge implement @args -exit $LASTEXITCODE +# Run implement command (Windows). Called from Cursor /i with user prompt as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +$ErrorActionPreference = "Stop" +& planforge implement @args +exit $LASTEXITCODE diff --git a/.cursor/skills/i/scripts/run_implement.py b/.cursor/skills/i/scripts/run_implement.py index dfb7a42..ec48581 100644 --- a/.cursor/skills/i/scripts/run_implement.py +++ b/.cursor/skills/i/scripts/run_implement.py @@ -1,2 +1,2 @@ -#!/usr/bin/env python3 -"""Run implement command (Codex provider).""" +#!/usr/bin/env python3 +"""Run implement command (Codex provider).""" diff --git a/.cursor/skills/i/scripts/run_implement.sh b/.cursor/skills/i/scripts/run_implement.sh index cc5f26f..ebc9157 100644 --- a/.cursor/skills/i/scripts/run_implement.sh +++ b/.cursor/skills/i/scripts/run_implement.sh @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -# Run implement command (uses implementer from planforge.json). Called from Cursor /i with user prompt as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -set -e -exec planforge implement "$@" +#!/usr/bin/env bash +# Run implement command (uses implementer from planforge.json). Called from Cursor /i with user prompt as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +set -e +exec planforge implement "$@" diff --git a/.cursor/skills/p/SKILL.md b/.cursor/skills/p/SKILL.md index 19aadd7..1522e74 100644 --- a/.cursor/skills/p/SKILL.md +++ b/.cursor/skills/p/SKILL.md @@ -1,34 +1,34 @@ ---- -name: p -description: Run PlanForge planning workflow by executing the bundled plan script. ---- - -# Plan (Planner) Skill - -PlanForge planning skill for Cursor. Use with `/p` to generate development plans. - -You must execute the PlanForge command path for every `/p` request. Do not draft a plan yourself. - -**When the user invokes /p with a goal (e.g. "/p design a tetris game"):** - -0. **Pre-fetch URLs (if goal contains URLs)** - If the goal contains any URLs (match `https?://[^\s]+`, including notion.so / notion.site): - - For each URL: use MCP tool **notion-fetch** (server `user-Notion`) with `{ "id": "" }` for Notion URLs; use **mcp_web_fetch** for other web URLs. Skip failed URLs; combine successful results into one markdown (e.g. `## \n\n` per URL). Keep this content for step 1. - -1. **Context file (one file: chat history + optional URL content)** - Derive a **slug** from the goal using the same rules as the plan command: lowercase ASCII letters, digits, hyphens only; at most 2 hyphens; English-only (e.g. `add-login`, `make-tetris`). Write **one** markdown file to the project's **context directory** (`.cursor/contexts`) under a dated subdirectory `YYYY-MM-DD` with filename `HHMM-.md` (use current time for HHMM). Do **not** over-summarize the conversation; use this format only: - ``` - # Cursor Chat History - User : {Question1} - Agent : {Answer1} - User : {Question2} - Agent : {Answer2} - ... - - Use this conversation to understand the user's intent and as reference when creating the plan. - ``` - If you fetched URL content in step 0, append it as a separate section (e.g. `## Fetched URLs` or per-URL headings) in the **same** file. Create the dated subdirectory if it does not exist. - -2. **Run the plan script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "This may take a while. Please wait." Then run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. - -3. **After it completes** - Read the generated `.plan.md` file and summarize/reference it in your reply. Do not start implementation. If execution fails, report the error output and suggest concrete fixes (for example `planforge init` or installing the configured provider CLI). - -If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute hand-written plan. +--- +name: p +description: Run PlanForge planning workflow by executing the bundled plan script. +--- + +# Plan (Planner) Skill + +PlanForge planning skill for Cursor. Use with `/p` to generate development plans. + +You must execute the PlanForge command path for every `/p` request. Do not draft a plan yourself. + +**When the user invokes /p with a goal (e.g. "/p design a tetris game"):** + +0. **Pre-fetch URLs (if goal contains URLs)** - If the goal contains any URLs (match `https?://[^\s]+`, including notion.so / notion.site): + - For each URL: use MCP tool **notion-fetch** (server `user-Notion`) with `{ "id": "" }` for Notion URLs; use **mcp_web_fetch** for other web URLs. Skip failed URLs; combine successful results into one markdown (e.g. `## \n\n` per URL). Keep this content for step 1. + +1. **Context file (one file: chat history + optional URL content)** - Derive a **slug** from the goal using the same rules as the plan command: lowercase ASCII letters, digits, hyphens only; at most 2 hyphens; English-only (e.g. `add-login`, `make-tetris`). Write **one** markdown file to the project's **context directory** (`.cursor/contexts`) under a dated subdirectory `YYYY-MM-DD` with filename `HHMM-.md` (use current time for HHMM). Do **not** over-summarize the conversation; use this format only: + ``` + # Cursor Chat History + User : {Question1} + Agent : {Answer1} + User : {Question2} + Agent : {Answer2} + ... + + Use this conversation to understand the user's intent and as reference when creating the plan. + ``` + If you fetched URL content in step 0, append it as a separate section (e.g. `## Fetched URLs` or per-URL headings) in the **same** file. Create the dated subdirectory if it does not exist. + +2. **Run the plan script in the foreground (required)** - **Before** running the command, output a one-line advisory in the **same language as the user's message** (e.g. if the user wrote in Korean, write in Korean; if in English, in English). Example: "This may take a while. Please wait." Then run **one** command only: no `cd`, no `&&` (PowerShell does not support `&&`). Terminal is already in workspace root. Use `.cursor/skills/p/scripts/run_plan.ps1 ""` on Windows or `.cursor/skills/p/scripts/run_plan.sh` on mac/Linux, or `planforge plan ""`. Run it **in the foreground** so that output streams in the Cursor chat sandbox terminal; do not run in the background. Pass the **same slug** so the plan output filename matches the context file: invoke as `planforge plan "" --slug ` (or ensure the script forwards `--slug `). The command generates `.cursor/plans/YYYY-MM-DD/{HHMM}-.plan.md` so that context file `HHMM-.md` and plan file `HHMM-.plan.md` use the same slug. Never create `.plan.md` content directly in chat. + +3. **After it completes** - Read the generated `.plan.md` file and summarize/reference it in your reply. Do not start implementation. If execution fails, report the error output and suggest concrete fixes (for example `planforge init` or installing the configured provider CLI). + +If script execution is blocked or fails, stop and return an error-focused response. Do not provide a substitute hand-written plan. diff --git a/.cursor/skills/p/prompts/system.md b/.cursor/skills/p/prompts/system.md index c19423a..de1ec98 100644 --- a/.cursor/skills/p/prompts/system.md +++ b/.cursor/skills/p/prompts/system.md @@ -1,16 +1,16 @@ -# Planner system prompt - -You are a development planner. Given a user goal, produce a structured plan as markdown. - -Output **only** the plan document, using exactly these level-2 headings and in this order: - -1. **Goal** – One or two sentences stating the objective. -2. **Assumptions** – What we assume about the codebase, environment, or constraints. -3. **Relevant Codebase Areas** – Directories, files, or modules that matter. -4. **Proposed Changes** – High-level list of changes (features, refactors, new files). -5. **Step-by-Step Plan** – Numbered steps to implement (clear and actionable). -6. **Files Likely to Change** – List of file paths or globs. -7. **Risks** – Possible issues or trade-offs. -8. **Validation Checklist** – How to verify the work (tests, manual checks). - -Use markdown headings (`## Goal`, `## Assumptions`, etc.). Do not add a title above the first heading; start with `## Goal`. +# Planner system prompt + +You are a development planner. Given a user goal, produce a structured plan as markdown. + +Output **only** the plan document, using exactly these level-2 headings and in this order: + +1. **Goal** – One or two sentences stating the objective. +2. **Assumptions** – What we assume about the codebase, environment, or constraints. +3. **Relevant Codebase Areas** – Directories, files, or modules that matter. +4. **Proposed Changes** – High-level list of changes (features, refactors, new files). +5. **Step-by-Step Plan** – Numbered steps to implement (clear and actionable). +6. **Files Likely to Change** – List of file paths or globs. +7. **Risks** – Possible issues or trade-offs. +8. **Validation Checklist** – How to verify the work (tests, manual checks). + +Use markdown headings (`## Goal`, `## Assumptions`, etc.). Do not add a title above the first heading; start with `## Goal`. diff --git a/.cursor/skills/p/scripts/run_plan.ps1 b/.cursor/skills/p/scripts/run_plan.ps1 index e10e49e..fb953aa 100644 --- a/.cursor/skills/p/scripts/run_plan.ps1 +++ b/.cursor/skills/p/scripts/run_plan.ps1 @@ -1,5 +1,5 @@ -# Run plan command (Windows). Called from Cursor /p with user goal as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -$ErrorActionPreference = "Stop" -& planforge plan @args -exit $LASTEXITCODE +# Run plan command (Windows). Called from Cursor /p with user goal as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +$ErrorActionPreference = "Stop" +& planforge plan @args +exit $LASTEXITCODE diff --git a/.cursor/skills/p/scripts/run_plan.py b/.cursor/skills/p/scripts/run_plan.py index a84538a..6b53fd9 100644 --- a/.cursor/skills/p/scripts/run_plan.py +++ b/.cursor/skills/p/scripts/run_plan.py @@ -1,2 +1,2 @@ -#!/usr/bin/env python3 -"""Run plan command (Claude provider).""" +#!/usr/bin/env python3 +"""Run plan command (Claude provider).""" diff --git a/.cursor/skills/p/scripts/run_plan.sh b/.cursor/skills/p/scripts/run_plan.sh index e1e88a6..5641a59 100644 --- a/.cursor/skills/p/scripts/run_plan.sh +++ b/.cursor/skills/p/scripts/run_plan.sh @@ -1,5 +1,5 @@ -#!/usr/bin/env bash -# Run plan command (provider from planforge.json). Called from Cursor /p with user goal as arguments. -# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. -set -e -exec planforge plan "$@" +#!/usr/bin/env bash +# Run plan command (provider from planforge.json). Called from Cursor /p with user goal as arguments. +# Context dir is taken from planforge.json (contextDir) or CLI default; do not hard-code it here. +set -e +exec planforge plan "$@" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a1081f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Shell scripts must use LF so they run on Mac/Linux (e.g. .cursor/skills/*/scripts/*.sh). +*.sh text eol=lf +# Cursor rules/skills: keep LF for cross-platform and CI (template–runtime comparison, script execution). +.cursor/rules/*.mdc text eol=lf +.cursor/skills/**/*.md text eol=lf +.cursor/skills/**/*.py text eol=lf +.cursor/skills/**/*.ps1 text eol=lf +templates/cursor/rules/*.mdc text eol=lf +templates/cursor/skills/**/*.md text eol=lf +templates/cursor/skills/**/*.py text eol=lf +templates/cursor/skills/**/*.ps1 text eol=lf From 2a350067eb515899f742bb4e5a88197c9a3de01f Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 18:47:59 +0900 Subject: [PATCH 7/8] fix : add codex error process (Authentication/Usage/Execution Failure) --- packages/cli-js/src/providers/codex.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/cli-js/src/providers/codex.ts b/packages/cli-js/src/providers/codex.ts index 91eff1b..5522af3 100644 --- a/packages/cli-js/src/providers/codex.ts +++ b/packages/cli-js/src/providers/codex.ts @@ -266,6 +266,16 @@ function runCodexExecStreaming( stdio: ["pipe", "pipe", "pipe"], }); const clearTimeoutRef = scheduleTimeout(child); + child.stdin.on("error", (err: NodeJS.ErrnoException) => { + if (err.code === "EPIPE") { + return; + } + clearTimeoutRef(); + if (!settled) { + settled = true; + reject(err); + } + }); child.stdin.write(fullPrompt, "utf-8"); child.stdin.end(); child.stdout?.on("data", handleStdout); From 961994cdd90ed1ae85d5ac66ee18a3accbc06a09 Mon Sep 17 00:00:00 2001 From: chucoding Date: Tue, 17 Mar 2026 18:50:05 +0900 Subject: [PATCH 8/8] fix : add codex error process (Authentication/Usage/Execution Failure)(python) --- packages/cli-py/planforge/providers/codex.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/cli-py/planforge/providers/codex.py b/packages/cli-py/planforge/providers/codex.py index 255f31b..8d0542e 100644 --- a/packages/cli-py/planforge/providers/codex.py +++ b/packages/cli-py/planforge/providers/codex.py @@ -201,8 +201,11 @@ def read_stderr(proc: subprocess.Popen) -> None: stderr=subprocess.PIPE, text=True, ) - proc.stdin.write(full_prompt) - proc.stdin.close() + try: + proc.stdin.write(full_prompt) + proc.stdin.close() + except BrokenPipeError: + pass temp_path = None t_out = threading.Thread(target=read_stdout, args=(proc,))