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
43 changes: 41 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,47 @@ on:
push:
branches:
- main
- 'fix/**'
- 'feat/**'
pull_request:
branches:
- main

jobs:
deploy:
deploy-staging:
if: github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Deploy to Staging
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env staging

- name: Comment staging URL
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 **Deployed to staging!**\n\nTest at: `https://devplan-mcp-server-staging.<your-subdomain>.workers.dev`\n\nUse `mcporter call https://devplan-mcp-server-staging...` to test.'
})

deploy-production:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -19,7 +57,8 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Deploy to Cloudflare Workers
- name: Deploy to Production
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: deploy --env=""
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,11 @@ CLAUDE.md

# Generated types
worker-configuration.d.ts
AGENTS.md\nHEARTBEAT.md\nIDENTITY.md\nSOUL.md\nTOOLS.md\nUSER.md

# OpenClaw workspace files
AGENTS.md
HEARTBEAT.md
IDENTITY.md
SOUL.md
TOOLS.md
USER.md
17 changes: 17 additions & 0 deletions heartbeat-state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"lastUpdate": "2026-02-13T18:04:00-08:00",
"activeWork": {
"branch": "fix/feature-grouping-146",
"status": "pr_ready",
"bugs": {
"completed": [130, 125, 126, 137, 138, 141, 146],
"remaining": [139, 140, 142]
}
},
"nextSteps": [
"Create PR for fix/feature-grouping-146",
"Fix language propagation (#139, #140)",
"Filter lessons by language (#142)"
],
"notes": "Language defaults not propagating - getLanguageDefaults called but results not applied"
}
38 changes: 38 additions & 0 deletions memory/2026-02-13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 2026-02-13 - DevPlan Bug Fixes Session

## Summary
Working through batch of bugs from testing (#137-#143, #146). PR #144 merged with 7 fixes.

## Completed Today
- PR #144 merged: #130, #125, #126, #137, #138, #141 fixes
- Branch `fix/feature-grouping-146` created - fixes feature grouping regression
- #146 root cause: `groupRelatedFeatures` only pushed groups that had items (endpoints), but features without endpoints are valid phases too

## Active Work
- **Branch:** `fix/feature-grouping-146` (builds pass, ready for PR)
- **Remaining bugs:** #139, #140, #142

## Bug Analysis
### #139 - CLAUDE.md ignores detected language
- `getLanguageDefaults` imported at line 12, called at line 222
- Language param passed but defaults not applied correctly
- Need to trace how language flows through generator pipeline

### #140 - Nonsensical file paths
- `.txt` extensions, Python naming conventions for Java projects
- Same root cause as #139 - language defaults not propagating

### #142 - Irrelevant lessons in plans
- Graph API/Cargo/RocksDB lessons appearing in Java plans
- Decision: Infer language from lesson content rather than add `languages` field

## Key Files
- `src/generators.ts` - main plan generation logic
- `src/language-defaults.ts` - language-specific defaults
- `src/adapters/claude.ts` - CLAUDE.md generator
- `src/templates.ts` - template system

## Next Session
1. Create PR for #146 fix
2. Fix language propagation in generators (#139, #140)
3. Filter lessons by inferred language (#142)
47 changes: 22 additions & 25 deletions src/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ import { getTemplate, PROJECT_TYPE_TASKS, findTemplate, type PhaseTemplate } fro
import { getLanguageDefaults, LanguageDefaults } from "./language-defaults";
import { generateReadmeDiagrams, formatDiagramsAsMarkdown } from "./readme-diagrams";

/**
* Inline completion instruction for Haiku agent.
* Must be included in each subtask since Haiku only sees the current context.
*/
const COMPLETION_INSTRUCTION = `
✅ **When Complete**: Change the subtask checkbox above from \`- [ ]\` to \`- [x]\` and fill in the Completion Notes.`;

export interface BriefInput {
name: string;
projectType: string;
Expand All @@ -34,7 +41,7 @@ export interface BriefInput {
*/
export interface TemplateKey {
projectType: "cli" | "web_app" | "api" | "library";
language: "python" | "typescript" | "javascript" | "go" | "rust" | "unknown";
language: DetectedLanguage; // Reuse the detected language type
variant?: "static" | "serverless" | "fullstack" | "minimal";
}

Expand All @@ -53,29 +60,8 @@ export function resolveTemplateKey(brief: ProjectBrief): TemplateKey {
// Detect language from must_use tech stack
const detectedLang = detectLanguage(brief.mustUseTech);

// Map detected language to TemplateKey language type
let language: TemplateKey["language"];
switch (detectedLang) {
case "python":
language = "python";
break;
case "typescript":
language = "typescript";
break;
case "javascript":
language = "javascript";
break;
default:
// Check for Go indicators
const techLower = brief.mustUseTech.map((t) => t.toLowerCase()).join(" ");
if (techLower.includes("go") || techLower.includes("golang")) {
language = "go";
} else if (techLower.includes("rust") || techLower.includes("cargo")) {
language = "rust";
} else {
language = "unknown";
}
}
// Use detected language directly - types are aligned
const language = detectedLang;

// Detect variant (placeholder until detectVariant is implemented in 1.1.2)
const variant = detectVariant(brief);
Expand Down Expand Up @@ -315,6 +301,7 @@ ${diagramsSection}
- **Build**: N/A (setup)
- **Branch**: feature/0-1-repo-setup
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---

Expand Down Expand Up @@ -348,6 +335,7 @@ ${successCriteria}
- **Build**: N/A
- **Branch**: feature/0-1-repo-setup
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---

Expand Down Expand Up @@ -386,6 +374,7 @@ ${lintingCriteria}
- **Build**: (linter: pass/fail)
- **Branch**: feature/0-2-dev-tools
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---

Expand Down Expand Up @@ -421,6 +410,7 @@ ${testingCriteria}
- **Build**: (test runner: pass/fail)
- **Branch**: feature/0-2-dev-tools
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---

Expand Down Expand Up @@ -530,6 +520,7 @@ ${featureList}
- **Build**: (pass/fail)
- **Branch**: feature/1-1-core-module
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---
${domainSubtasks}
Expand Down Expand Up @@ -574,6 +565,7 @@ ${domainSubtasks}
- **Build**: (pass/fail)
- **Branch**: feature/1-1-core-module
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---

Expand Down Expand Up @@ -650,6 +642,7 @@ ${allSchemaItems.map(item => `- [ ] \`${item.name}\` table can be created and qu
- **Build**: (pass/fail)
- **Branch**: feature/1-1-core-module
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---
`);
Expand Down Expand Up @@ -705,6 +698,7 @@ ${allApiItems.map(item => `- [ ] \`${item.name}\` returns expected response form
- **Build**: (pass/fail)
- **Branch**: feature/1-1-core-module
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---
`);
Expand Down Expand Up @@ -760,6 +754,7 @@ ${allPipelineItems.map(item => `- [ ] ${item.name} completes successfully`).join
- **Build**: (pass/fail)
- **Branch**: feature/1-1-core-module
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---
`);
Expand Down Expand Up @@ -1648,6 +1643,7 @@ ${successCriteria}
- **Build**: (ruff: pass/fail, mypy: pass/fail)
- **Branch**: feature/${task.id}-${subtask.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 20)}
- **Notes**: (any additional context)
${COMPLETION_INSTRUCTION}

---`;
})
Expand Down Expand Up @@ -3330,7 +3326,8 @@ function groupRelatedFeatures(features: string[]): Array<{ name: string; items:
}
} else {
// This is a regular feature - finalize previous group and start new one
if (currentGroup && currentGroup.items.length > 0) {
if (currentGroup) {
// Always push the previous group (features without endpoints are valid phases)
groups.push(currentGroup);
}
currentGroup = { name: trimmed, items: [] };
Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ interface Env {
SESSION_INACTIVITY_TTL_DAYS: string;
SESSION_ABSOLUTE_TTL_DAYS: string;
CLEANUP_CHECK_HOURS: string;
ENVIRONMENT?: string; // "staging" for staging environment
DEVPLAN_KV: KVNamespace;
MCP_OBJECT: DurableObjectNamespace;
// Cloudflare Analytics API (optional - for dashboard)
Expand Down Expand Up @@ -2235,8 +2236,8 @@ export default {
const isRootDomain = url.hostname === "devplanmcp.store";
const isWorkersDevDomain = url.hostname.endsWith(".workers.dev");

// Redirect workers.dev to MCP subdomain for MCP endpoints, root for others
if (isWorkersDevDomain) {
// Redirect workers.dev to custom domain (skip for staging environment)
if (isWorkersDevDomain && env.ENVIRONMENT !== "staging") {
const isMcpPath = url.pathname === "/sse" || url.pathname === "/sse/message" || url.pathname === "/mcp";
const targetDomain = isMcpPath ? "https://mcp.devplanmcp.store" : "https://devplanmcp.store";
const newUrl = new URL(url.pathname + url.search, targetDomain);
Expand Down
Loading