Step 418: add workflow MCP prompt templates
This commit is contained in:
@@ -2641,4 +2641,13 @@ target_link_libraries(step417_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step418_test tests/step418_test.cpp)
|
||||
target_include_directories(step418_test PRIVATE src)
|
||||
target_link_libraries(step418_test PRIVATE
|
||||
nlohmann_json::nlohmann_json
|
||||
unofficial::tree-sitter::tree-sitter
|
||||
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||
|
||||
159
editor/src/MCPWorkflowPrompts.h
Normal file
159
editor/src/MCPWorkflowPrompts.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct MCPWorkflowPromptTemplate {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string userPrompt;
|
||||
std::vector<std::string> expectedTools;
|
||||
};
|
||||
|
||||
class MCPWorkflowPrompts {
|
||||
public:
|
||||
static std::vector<MCPWorkflowPromptTemplate> templates() {
|
||||
return {
|
||||
{
|
||||
"architect_project",
|
||||
"Analyze codebase and create annotated project skeleton.",
|
||||
"Protocol:\n"
|
||||
"1. Run whetstone_infer_annotations over key modules.\n"
|
||||
"2. Create structural scaffold with whetstone_create_skeleton.\n"
|
||||
"3. Build initial task plan using whetstone_create_workflow.\n"
|
||||
"4. Explain architecture invariants and routing assumptions.\n"
|
||||
"Goal: Analyze {{project_name}} and produce an annotated skeleton.",
|
||||
{"whetstone_infer_annotations", "whetstone_create_skeleton", "whetstone_create_workflow"}
|
||||
},
|
||||
{
|
||||
"modernize_module",
|
||||
"Modernize legacy module with workflow-guided safety.",
|
||||
"Protocol:\n"
|
||||
"1. Infer annotations and constraints for {{module_name}} with whetstone_infer_annotations.\n"
|
||||
"2. Run automatic safe tasks via whetstone_orchestrate_run_deterministic.\n"
|
||||
"3. Fetch unresolved items with whetstone_get_blockers.\n"
|
||||
"4. Submit agent changes with whetstone_submit_result.\n"
|
||||
"Goal: Modernize module while preserving behavior and auditability.",
|
||||
{"whetstone_infer_annotations", "whetstone_orchestrate_run_deterministic",
|
||||
"whetstone_get_blockers", "whetstone_submit_result"}
|
||||
},
|
||||
{
|
||||
"port_to_language",
|
||||
"Port module between languages through workflow routing.",
|
||||
"Protocol:\n"
|
||||
"1. Read source semantics and infer annotations with whetstone_infer_annotations.\n"
|
||||
"2. Project using whetstone_project_language from {{source_language}} to {{target_language}}.\n"
|
||||
"3. Plan remaining edits with whetstone_create_workflow.\n"
|
||||
"4. Execute deterministic tasks and report blockers with whetstone_get_blockers.\n"
|
||||
"Goal: Port code to target language with semantic parity.",
|
||||
{"whetstone_infer_annotations", "whetstone_project_language",
|
||||
"whetstone_create_workflow", "whetstone_get_blockers"}
|
||||
},
|
||||
{
|
||||
"add_feature",
|
||||
"Add feature through orchestrated skeleton-first workflow.",
|
||||
"Protocol:\n"
|
||||
"1. Add feature skeleton nodes with whetstone_create_skeleton.\n"
|
||||
"2. Create execution plan via whetstone_create_workflow.\n"
|
||||
"3. Execute deterministic tasks with whetstone_orchestrate_run_deterministic.\n"
|
||||
"4. Track progress using whetstone_get_progress.\n"
|
||||
"Goal: Implement {{feature_name}} with explicit review checkpoints.",
|
||||
{"whetstone_create_skeleton", "whetstone_create_workflow",
|
||||
"whetstone_orchestrate_run_deterministic", "whetstone_get_progress"}
|
||||
},
|
||||
{
|
||||
"review_pending",
|
||||
"Review and approve/reject pending workflow items.",
|
||||
"Protocol:\n"
|
||||
"1. Load pending review items using whetstone_get_review_queue.\n"
|
||||
"2. For each item, open full details with whetstone_get_review_context.\n"
|
||||
"3. Approve with whetstone_approve_item or reject with whetstone_reject_item.\n"
|
||||
"4. Summarize accepted/rejected decisions and rationale.\n"
|
||||
"Goal: Clear review queue with traceable decisions.",
|
||||
{"whetstone_get_review_queue", "whetstone_get_review_context",
|
||||
"whetstone_approve_item", "whetstone_reject_item"}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static bool hasTemplate(const std::string& name) {
|
||||
for (const auto& t : templates()) if (t.name == name) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static MCPWorkflowPromptTemplate getTemplate(const std::string& name) {
|
||||
for (const auto& t : templates()) if (t.name == name) return t;
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::string renderPrompt(const std::string& name,
|
||||
const std::map<std::string, std::string>& params) {
|
||||
auto t = getTemplate(name);
|
||||
std::string out = t.userPrompt;
|
||||
for (const auto& [k, v] : params) {
|
||||
replaceAll(out, "{{" + k + "}}", v);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool validate(const MCPWorkflowPromptTemplate& t, std::string& error) {
|
||||
if (t.name.empty()) { error = "template name is empty"; return false; }
|
||||
if (t.userPrompt.find("Protocol:") == std::string::npos) {
|
||||
error = "missing protocol section";
|
||||
return false;
|
||||
}
|
||||
if (t.expectedTools.empty()) {
|
||||
error = "expected tools list is empty";
|
||||
return false;
|
||||
}
|
||||
for (const auto& tool : t.expectedTools) {
|
||||
if (t.userPrompt.find(tool) == std::string::npos) {
|
||||
error = "prompt does not reference required tool: " + tool;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static json manifestJson() {
|
||||
json arr = json::array();
|
||||
for (const auto& t : templates()) {
|
||||
arr.push_back({
|
||||
{"name", t.name},
|
||||
{"description", t.description},
|
||||
{"expectedTools", t.expectedTools}
|
||||
});
|
||||
}
|
||||
return {{"templates", arr}};
|
||||
}
|
||||
|
||||
static bool writeTemplateFiles(const std::string& outputDir) {
|
||||
std::filesystem::create_directories(outputDir);
|
||||
for (const auto& t : templates()) {
|
||||
std::ofstream out(std::filesystem::path(outputDir) / (t.name + ".prompt"));
|
||||
if (!out.is_open()) return false;
|
||||
out << "# " << t.name << "\n\n";
|
||||
out << "description:\n" << t.description << "\n\n";
|
||||
out << "user:\n" << t.userPrompt << "\n\n";
|
||||
out << "expected_tools:\n";
|
||||
for (const auto& tool : t.expectedTools) out << "- " << tool << "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void replaceAll(std::string& text, const std::string& from, const std::string& to) {
|
||||
if (from.empty()) return;
|
||||
size_t pos = 0;
|
||||
while ((pos = text.find(from, pos)) != std::string::npos) {
|
||||
text.replace(pos, from.size(), to);
|
||||
pos += to.size();
|
||||
}
|
||||
}
|
||||
};
|
||||
146
editor/tests/step418_test.cpp
Normal file
146
editor/tests/step418_test.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Step 418: Workflow Prompt Templates Tests (12 tests)
|
||||
|
||||
#include "MCPWorkflowPrompts.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
static int passed = 0, failed = 0;
|
||||
#define TEST(name) { std::cout << " " << #name << "... "; }
|
||||
#define PASS() { std::cout << "PASS\n"; ++passed; }
|
||||
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
|
||||
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
|
||||
|
||||
void test_templates_include_required_names() {
|
||||
TEST(templates_include_required_names);
|
||||
CHECK(MCPWorkflowPrompts::hasTemplate("architect_project"), "missing architect_project");
|
||||
CHECK(MCPWorkflowPrompts::hasTemplate("modernize_module"), "missing modernize_module");
|
||||
CHECK(MCPWorkflowPrompts::hasTemplate("port_to_language"), "missing port_to_language");
|
||||
CHECK(MCPWorkflowPrompts::hasTemplate("add_feature"), "missing add_feature");
|
||||
CHECK(MCPWorkflowPrompts::hasTemplate("review_pending"), "missing review_pending");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_architect_project_tools_present() {
|
||||
TEST(architect_project_tools_present);
|
||||
auto t = MCPWorkflowPrompts::getTemplate("architect_project");
|
||||
CHECK(t.userPrompt.find("whetstone_infer_annotations") != std::string::npos, "missing infer tool");
|
||||
CHECK(t.userPrompt.find("whetstone_create_skeleton") != std::string::npos, "missing skeleton tool");
|
||||
CHECK(t.userPrompt.find("whetstone_create_workflow") != std::string::npos, "missing workflow tool");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_modernize_module_tools_present() {
|
||||
TEST(modernize_module_tools_present);
|
||||
auto t = MCPWorkflowPrompts::getTemplate("modernize_module");
|
||||
CHECK(t.userPrompt.find("whetstone_orchestrate_run_deterministic") != std::string::npos,
|
||||
"missing deterministic run tool");
|
||||
CHECK(t.userPrompt.find("whetstone_get_blockers") != std::string::npos, "missing blockers tool");
|
||||
CHECK(t.userPrompt.find("whetstone_submit_result") != std::string::npos, "missing submit tool");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_port_template_mentions_source_target() {
|
||||
TEST(port_template_mentions_source_target);
|
||||
auto rendered = MCPWorkflowPrompts::renderPrompt(
|
||||
"port_to_language",
|
||||
{{"source_language", "python"}, {"target_language", "rust"}});
|
||||
CHECK(rendered.find("python") != std::string::npos, "missing source language replacement");
|
||||
CHECK(rendered.find("rust") != std::string::npos, "missing target language replacement");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_add_feature_template_progress_tools_present() {
|
||||
TEST(add_feature_template_progress_tools_present);
|
||||
auto t = MCPWorkflowPrompts::getTemplate("add_feature");
|
||||
CHECK(t.userPrompt.find("whetstone_create_workflow") != std::string::npos, "missing workflow");
|
||||
CHECK(t.userPrompt.find("whetstone_get_progress") != std::string::npos, "missing progress");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_review_pending_template_review_tools_present() {
|
||||
TEST(review_pending_template_review_tools_present);
|
||||
auto t = MCPWorkflowPrompts::getTemplate("review_pending");
|
||||
CHECK(t.userPrompt.find("whetstone_get_review_queue") != std::string::npos, "missing review queue");
|
||||
CHECK(t.userPrompt.find("whetstone_get_review_context") != std::string::npos, "missing review context");
|
||||
CHECK(t.userPrompt.find("whetstone_approve_item") != std::string::npos, "missing approve");
|
||||
CHECK(t.userPrompt.find("whetstone_reject_item") != std::string::npos, "missing reject");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_render_prompt_substitutes_project_name() {
|
||||
TEST(render_prompt_substitutes_project_name);
|
||||
std::string rendered = MCPWorkflowPrompts::renderPrompt(
|
||||
"architect_project", {{"project_name", "AlphaRepo"}});
|
||||
CHECK(rendered.find("AlphaRepo") != std::string::npos, "project_name not substituted");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_render_prompt_keeps_unprovided_placeholders() {
|
||||
TEST(render_prompt_keeps_unprovided_placeholders);
|
||||
std::string rendered = MCPWorkflowPrompts::renderPrompt("add_feature", {});
|
||||
CHECK(rendered.find("{{feature_name}}") != std::string::npos,
|
||||
"unprovided placeholder should remain explicit");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_validate_rejects_empty_tools() {
|
||||
TEST(validate_rejects_empty_tools);
|
||||
MCPWorkflowPromptTemplate t;
|
||||
t.name = "bad";
|
||||
t.userPrompt = "Protocol:\n1. do a thing";
|
||||
std::string err;
|
||||
CHECK(!MCPWorkflowPrompts::validate(t, err), "expected validation failure");
|
||||
CHECK(!err.empty(), "expected error message");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_manifest_json_contains_all_templates() {
|
||||
TEST(manifest_json_contains_all_templates);
|
||||
json m = MCPWorkflowPrompts::manifestJson();
|
||||
CHECK(m.contains("templates"), "missing templates field");
|
||||
CHECK(m["templates"].is_array(), "templates should be array");
|
||||
CHECK(m["templates"].size() == 5, "expected five templates");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_write_template_files_to_directory() {
|
||||
TEST(write_template_files_to_directory);
|
||||
auto outDir = std::filesystem::temp_directory_path() / "whetstone_step418_prompts";
|
||||
CHECK(MCPWorkflowPrompts::writeTemplateFiles(outDir.string()), "writeTemplateFiles failed");
|
||||
CHECK(std::filesystem::exists(outDir / "architect_project.prompt"), "missing architect prompt file");
|
||||
CHECK(std::filesystem::exists(outDir / "review_pending.prompt"), "missing review prompt file");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_all_templates_validate_successfully() {
|
||||
TEST(all_templates_validate_successfully);
|
||||
for (const auto& t : MCPWorkflowPrompts::templates()) {
|
||||
std::string err;
|
||||
CHECK(MCPWorkflowPrompts::validate(t, err), "template validation failed");
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 418: Workflow Prompt Templates Tests\n";
|
||||
|
||||
test_templates_include_required_names(); // 1
|
||||
test_architect_project_tools_present(); // 2
|
||||
test_modernize_module_tools_present(); // 3
|
||||
test_port_template_mentions_source_target(); // 4
|
||||
test_add_feature_template_progress_tools_present(); // 5
|
||||
test_review_pending_template_review_tools_present(); // 6
|
||||
test_render_prompt_substitutes_project_name(); // 7
|
||||
test_render_prompt_keeps_unprovided_placeholders(); // 8
|
||||
test_validate_rejects_empty_tools(); // 9
|
||||
test_manifest_json_contains_all_templates(); // 10
|
||||
test_write_template_files_to_directory(); // 11
|
||||
test_all_templates_validate_successfully(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed)
|
||||
<< " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
44
progress.md
44
progress.md
@@ -4278,6 +4278,50 @@ inspection without adding a custom plugin binary.
|
||||
- `editor/src/MCPServer.h` (`1679` > `600`)
|
||||
- `editor/src/HeadlessAgentRPCHandler.h` (`2629` > `600`)
|
||||
|
||||
### Step 418: Workflow Prompt Templates
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Added workflow-specific MCP prompt templates aligned to Sprint 18a's named
|
||||
flows (`architect_project`, `modernize_module`, `port_to_language`,
|
||||
`add_feature`, `review_pending`) with explicit tool references and rendering.
|
||||
|
||||
**Files created:**
|
||||
- `editor/src/MCPWorkflowPrompts.h` — workflow prompt-template support:
|
||||
- canonical template catalog (5 templates)
|
||||
- parameterized prompt rendering (`{{...}}` substitution)
|
||||
- template validation (protocol section + required tool mentions)
|
||||
- JSON manifest export for client/tooling discovery
|
||||
- prompt-file writer for emitting portable `.prompt` files
|
||||
- `editor/tests/step418_test.cpp` — 12 tests covering:
|
||||
1. required template-name presence
|
||||
2. `architect_project` tool references
|
||||
3. `modernize_module` tool references
|
||||
4. `port_to_language` source/target substitution
|
||||
5. `add_feature` progress workflow references
|
||||
6. `review_pending` review queue/context/approve/reject references
|
||||
7. parameter substitution behavior
|
||||
8. unresolved placeholder behavior when param omitted
|
||||
9. validation failure on empty tool list
|
||||
10. manifest structure and template count
|
||||
11. template-file write-out
|
||||
12. validation success for all shipped templates
|
||||
|
||||
**Files modified:**
|
||||
- `editor/CMakeLists.txt` — `step418_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `step418_test` — PASS (12/12) new step coverage
|
||||
- `step417_test` — PASS (12/12) regression coverage
|
||||
- `step416_test` — PASS (8/8) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/MCPWorkflowPrompts.h` within header-size limit (`159` <= `600`)
|
||||
- `editor/tests/step418_test.cpp` within test-file size guidance (`146` lines)
|
||||
- Legacy oversized headers persist:
|
||||
- `editor/src/ast/Serialization.h` (`1427` > `600`)
|
||||
- `editor/src/MCPServer.h` (`1679` > `600`)
|
||||
- `editor/src/HeadlessAgentRPCHandler.h` (`2629` > `600`)
|
||||
|
||||
# Roadmap Planning — Sprints 12-25+
|
||||
|
||||
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)
|
||||
|
||||
Reference in New Issue
Block a user