Submatter/packages/database/src/markdown-backlog/paths.ts
Randall Stillwell 663bc77afe feat: ECHODO app shell, Coolify deploy, Authentik + Umami
Bundles in-flight ECHODO work with the Coolify deployment configuration:

App
- New routes: ai, forms, planner, settings (templates/types), teams,
  doc detail, whiteboard detail
- New components: app shell rework (icon-rail, top-header), forms
  builder/renderer/responses, types manager, objects creation dialog,
  card primitive, form + overview views
- New tRPC routers: favorites, forms, types, workspaces; updates to
  health and objects routers
- Markdown backlog sync (packages/database) + cursor-sync schema/migrations
- Schema additions: forms, types, favorites, markdown_backlog, cursor_sync
- Initial Drizzle migrations checked in

Deployment
- docker/docker-compose.coolify.yml: drops bundled Postgres/Redis
  (uses CT 102 shared services), removes host port mappings, adds
  Coolify SERVICE_FQDN_* magic vars for web + collab
- .env.example rewritten as the full ECHODO/Coolify variable manifest
- NextAuth gains an Authentik OIDC provider (gated on env presence)
- Root layout injects Umami tracking script when configured;
  metadata title flipped to ECHODO

Security
- .gitignore expanded to exclude AGENT-DEPLOY.md, .env.*, secrets/,
  credentials.*, *.key, *.crt, *.pem, ssh keys

Made-with: Cursor
2026-04-26 14:34:34 -05:00

67 lines
2.2 KiB
TypeScript

import { sep } from "node:path";
/** POSIX-style path relative to repository root (e.g. `plans/Plan-a/Task-b.md`). */
export function toRepoPathPosix(relativeFromRepoRoot: string): string {
return relativeFromRepoRoot.split(sep).join("/");
}
/**
* Whether this repo-relative path should be imported as backlog markdown.
* Expects POSIX `plans/...` paths.
*/
export function isTrackedBacklogMarkdown(repoPathPosix: string): boolean {
if (!repoPathPosix.startsWith("plans/")) return false;
if (repoPathPosix === "plans/README.md") return false;
const base = repoPathPosix.split("/").pop() ?? "";
return (
base.startsWith("Plan-") || base.startsWith("Epic-") || base.startsWith("Task-")
);
}
/**
* Parent document path for hierarchy links, or `null` for plan roots.
*/
export function parentRepoPath(repoPathPosix: string): string | null {
const parts = repoPathPosix.split("/").filter(Boolean);
if (parts.length < 3 || parts[0] !== "plans") return null;
const planDir = parts[1];
if (!planDir.startsWith("Plan-")) return null;
if (parts.length === 3) {
const file = parts[2];
if (file.startsWith("Plan-") && file.endsWith(".md")) return null;
}
if (parts.length === 4) {
const file = parts[3];
if (file.startsWith("Epic-") && file.endsWith(".md")) {
return `plans/${planDir}/${planDir}.md`;
}
}
if (parts.length === 5) {
const epicDir = parts[2];
const file = parts[4];
if (epicDir.startsWith("Epic-") && file.startsWith("Task-") && file.endsWith(".md")) {
return `plans/${planDir}/${epicDir}/${epicDir}.md`;
}
}
return null;
}
export function planSlugFromPath(repoPathPosix: string): string | null {
const parts = repoPathPosix.split("/").filter(Boolean);
if (parts.length < 2 || parts[0] !== "plans") return null;
const planDir = parts[1];
return planDir.startsWith("Plan-") ? planDir.replace(/^Plan-/, "") : null;
}
export function epicFolderFromPath(repoPathPosix: string): string | null {
const parts = repoPathPosix.split("/").filter(Boolean);
if (parts.length < 3 || parts[0] !== "plans") return null;
const epicDir = parts[2];
if (parts.length >= 4 && epicDir.startsWith("Epic-")) return epicDir.replace(/^Epic-/, "");
return null;
}