@byrde/cursor 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/assets/agents/architect.md +128 -0
- package/assets/agents/developer.md +84 -0
- package/assets/agents/planner.md +59 -0
- package/assets/agents/tester.md +77 -0
- package/assets/rules/global.mdc +180 -0
- package/assets/rules/init.mdc +26 -0
- package/assets/templates/backlog.md +18 -0
- package/assets/templates/design.md +25 -0
- package/assets/templates/overview.md +38 -0
- package/assets/templates/testability/README.md +102 -0
- package/dist/cli.js +1241 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +465 -0
- package/dist/index.js.map +1 -0
- package/dist/sync-workflow.js +237 -0
- package/dist/sync-workflow.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/sync-workflow.ts
|
|
4
|
+
import { cpSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
// src/domain/config.ts
|
|
9
|
+
var PROJECT_CONFIG_FILENAME = "workflow.json";
|
|
10
|
+
function createDefaultWorkflowModels() {
|
|
11
|
+
return {
|
|
12
|
+
planner: "gpt-5.4-high",
|
|
13
|
+
architect: "gpt-5.4-high",
|
|
14
|
+
developer: "composer-2-fast",
|
|
15
|
+
tester: "composer-2-fast"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createDefaultProjectConfig() {
|
|
19
|
+
return {
|
|
20
|
+
backlog: {
|
|
21
|
+
provider: "file",
|
|
22
|
+
file: {
|
|
23
|
+
path: "docs/backlog.md"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
workflow: {
|
|
27
|
+
defaults: {
|
|
28
|
+
architectReview: "required",
|
|
29
|
+
testing: "required"
|
|
30
|
+
},
|
|
31
|
+
models: createDefaultWorkflowModels()
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
var DEFAULT_PROJECT_CONFIG = createDefaultProjectConfig();
|
|
36
|
+
function isNonEmptyString(v) {
|
|
37
|
+
return typeof v === "string" && v.trim().length > 0;
|
|
38
|
+
}
|
|
39
|
+
function normalizeBacklog(raw, base) {
|
|
40
|
+
if (!raw || typeof raw !== "object") {
|
|
41
|
+
return base;
|
|
42
|
+
}
|
|
43
|
+
const b = raw;
|
|
44
|
+
const provider = b.provider === "github-issues" || b.provider === "file" ? b.provider : base.provider;
|
|
45
|
+
if (provider === "file") {
|
|
46
|
+
const fileRaw = b.file;
|
|
47
|
+
const pathFrom = fileRaw && typeof fileRaw === "object" && isNonEmptyString(fileRaw.path) ? String(fileRaw.path).trim() : base.file.path;
|
|
48
|
+
return { provider: "file", file: { path: pathFrom } };
|
|
49
|
+
}
|
|
50
|
+
const ghRaw = b["github-issues"];
|
|
51
|
+
const githubOptionalDefaults = {
|
|
52
|
+
priorityField: "Priority",
|
|
53
|
+
statusField: "Status",
|
|
54
|
+
mcpServerName: "github"
|
|
55
|
+
};
|
|
56
|
+
function parsePositiveInt(v) {
|
|
57
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) {
|
|
58
|
+
return Math.trunc(v);
|
|
59
|
+
}
|
|
60
|
+
if (isNonEmptyString(v)) {
|
|
61
|
+
const n = Number.parseInt(String(v).trim(), 10);
|
|
62
|
+
if (Number.isFinite(n) && n > 0) {
|
|
63
|
+
return n;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
if (ghRaw && typeof ghRaw === "object") {
|
|
69
|
+
const g = ghRaw;
|
|
70
|
+
const repository = isNonEmptyString(g.repository) ? g.repository.trim() : void 0;
|
|
71
|
+
const projectNumber = parsePositiveInt(g.projectNumber);
|
|
72
|
+
if (repository !== void 0 && projectNumber !== void 0) {
|
|
73
|
+
const priorityField = isNonEmptyString(g.priorityField) ? g.priorityField.trim() : githubOptionalDefaults.priorityField;
|
|
74
|
+
const statusField = isNonEmptyString(g.statusField) ? g.statusField.trim() : githubOptionalDefaults.statusField;
|
|
75
|
+
const labelRaw = g.label;
|
|
76
|
+
const label = isNonEmptyString(labelRaw) ? labelRaw.trim() : void 0;
|
|
77
|
+
return {
|
|
78
|
+
provider: "github-issues",
|
|
79
|
+
"github-issues": {
|
|
80
|
+
repository,
|
|
81
|
+
projectNumber,
|
|
82
|
+
projectOwner: isNonEmptyString(g.projectOwner) ? g.projectOwner.trim() : void 0,
|
|
83
|
+
priorityField,
|
|
84
|
+
statusField,
|
|
85
|
+
...label !== void 0 ? { label } : {},
|
|
86
|
+
mcpServerName: isNonEmptyString(g.mcpServerName) ? g.mcpServerName.trim() : githubOptionalDefaults.mcpServerName
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return base;
|
|
92
|
+
}
|
|
93
|
+
function normalizeWorkflow(raw, base) {
|
|
94
|
+
const models = { ...base.models };
|
|
95
|
+
let architectReview = base.defaults.architectReview;
|
|
96
|
+
let testing = base.defaults.testing;
|
|
97
|
+
if (raw && typeof raw === "object") {
|
|
98
|
+
const w = raw;
|
|
99
|
+
const d = w.defaults;
|
|
100
|
+
if (d && typeof d === "object") {
|
|
101
|
+
const def = d;
|
|
102
|
+
if (def.architectReview === "required" || def.architectReview === "optional") {
|
|
103
|
+
architectReview = def.architectReview;
|
|
104
|
+
}
|
|
105
|
+
if (def.testing === "required" || def.testing === "optional") {
|
|
106
|
+
testing = def.testing;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const m = w.models;
|
|
110
|
+
if (m && typeof m === "object") {
|
|
111
|
+
const mo = m;
|
|
112
|
+
const pick = (key) => {
|
|
113
|
+
const v = mo[key];
|
|
114
|
+
return isNonEmptyString(v) ? v.trim() : base.models[key];
|
|
115
|
+
};
|
|
116
|
+
models.planner = pick("planner");
|
|
117
|
+
models.architect = pick("architect");
|
|
118
|
+
models.developer = pick("developer");
|
|
119
|
+
models.tester = pick("tester");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
defaults: { architectReview, testing },
|
|
124
|
+
models
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function normalizeProjectConfig(raw) {
|
|
128
|
+
const base = createDefaultProjectConfig();
|
|
129
|
+
if (!raw || typeof raw !== "object") {
|
|
130
|
+
return base;
|
|
131
|
+
}
|
|
132
|
+
const o = raw;
|
|
133
|
+
return {
|
|
134
|
+
backlog: normalizeBacklog(o.backlog, base.backlog),
|
|
135
|
+
workflow: normalizeWorkflow(o.workflow, base.workflow)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/domain/asset-manifest.ts
|
|
140
|
+
var DEFAULT_MANIFEST = [
|
|
141
|
+
{
|
|
142
|
+
source: "assets/agents/architect.md",
|
|
143
|
+
target: "agents/architect.md",
|
|
144
|
+
renderAgent: "architect"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
source: "assets/agents/developer.md",
|
|
148
|
+
target: "agents/developer.md",
|
|
149
|
+
renderAgent: "developer"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
source: "assets/agents/planner.md",
|
|
153
|
+
target: "agents/planner.md",
|
|
154
|
+
renderAgent: "planner"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
source: "assets/agents/tester.md",
|
|
158
|
+
target: "agents/tester.md",
|
|
159
|
+
renderAgent: "tester"
|
|
160
|
+
},
|
|
161
|
+
{ source: "assets/rules/global.mdc", target: "rules/global.mdc" },
|
|
162
|
+
{ source: "assets/rules/init.mdc", target: "rules/init.mdc" },
|
|
163
|
+
{ source: "assets/templates/backlog.md", target: "templates/backlog.md" },
|
|
164
|
+
{ source: "assets/templates/design.md", target: "templates/design.md" },
|
|
165
|
+
{ source: "assets/templates/overview.md", target: "templates/overview.md" },
|
|
166
|
+
{
|
|
167
|
+
source: "assets/templates/testability/README.md",
|
|
168
|
+
target: "templates/testability/README.md"
|
|
169
|
+
}
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
// src/infrastructure/agent-template.ts
|
|
173
|
+
var AGENT_MODEL_PLACEHOLDER = "{{MODEL}}";
|
|
174
|
+
function renderAgentTemplate(templateUtf8, model) {
|
|
175
|
+
if (!templateUtf8.includes(AGENT_MODEL_PLACEHOLDER)) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Agent template is missing ${AGENT_MODEL_PLACEHOLDER} placeholder`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return templateUtf8.replaceAll(AGENT_MODEL_PLACEHOLDER, model);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/infrastructure/project-config-store.ts
|
|
184
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
185
|
+
import path from "path";
|
|
186
|
+
var LEGACY_PROJECT_CONFIG_FILENAME = "byrde.json";
|
|
187
|
+
function projectConfigPath(cwd) {
|
|
188
|
+
return path.join(cwd, ".cursor", PROJECT_CONFIG_FILENAME);
|
|
189
|
+
}
|
|
190
|
+
function loadProjectConfig(cwd) {
|
|
191
|
+
const configPath = projectConfigPath(cwd);
|
|
192
|
+
const legacyPath = path.join(cwd, ".cursor", LEGACY_PROJECT_CONFIG_FILENAME);
|
|
193
|
+
if (existsSync(configPath)) {
|
|
194
|
+
return normalizeProjectConfig(
|
|
195
|
+
JSON.parse(readFileSync(configPath, "utf8"))
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (existsSync(legacyPath)) {
|
|
199
|
+
return normalizeProjectConfig(
|
|
200
|
+
JSON.parse(readFileSync(legacyPath, "utf8"))
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/sync-workflow.ts
|
|
207
|
+
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
208
|
+
function packageRootFromScript() {
|
|
209
|
+
return path2.resolve(__dirname, "..");
|
|
210
|
+
}
|
|
211
|
+
function isRenderedAgentEntry(entry) {
|
|
212
|
+
return entry.renderAgent !== void 0;
|
|
213
|
+
}
|
|
214
|
+
function main() {
|
|
215
|
+
const cwd = process.cwd();
|
|
216
|
+
const packageRoot = packageRootFromScript();
|
|
217
|
+
const config = normalizeProjectConfig(
|
|
218
|
+
loadProjectConfig(cwd) ?? createDefaultProjectConfig()
|
|
219
|
+
);
|
|
220
|
+
for (const entry of DEFAULT_MANIFEST) {
|
|
221
|
+
const src = path2.join(packageRoot, entry.source);
|
|
222
|
+
const dest = path2.join(cwd, ".cursor", entry.target);
|
|
223
|
+
mkdirSync2(path2.dirname(dest), { recursive: true });
|
|
224
|
+
if (isRenderedAgentEntry(entry)) {
|
|
225
|
+
const tpl = readFileSync2(src, "utf8");
|
|
226
|
+
const model = config.workflow.models[entry.renderAgent];
|
|
227
|
+
writeFileSync2(dest, renderAgentTemplate(tpl, model), "utf8");
|
|
228
|
+
} else {
|
|
229
|
+
cpSync(src, dest);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
console.log(
|
|
233
|
+
"Synced workflow assets into .cursor/ (agents rendered from templates; other files copied)."
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
main();
|
|
237
|
+
//# sourceMappingURL=sync-workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sync-workflow.ts","../src/domain/config.ts","../src/domain/asset-manifest.ts","../src/infrastructure/agent-template.ts","../src/infrastructure/project-config-store.ts"],"sourcesContent":["/**\n * Renders agent templates and copies other workflow assets from `assets/` into\n * `<cwd>/.cursor/` for local dogfooding. Does not update `.managed.json` (use\n * `init` for managed installs).\n */\nimport { cpSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createDefaultProjectConfig, normalizeProjectConfig } from \"./domain/config.js\";\nimport type { AssetEntry } from \"./domain/asset-manifest.js\";\nimport { DEFAULT_MANIFEST } from \"./domain/asset-manifest.js\";\nimport { renderAgentTemplate } from \"./infrastructure/agent-template.js\";\nimport { loadProjectConfig } from \"./infrastructure/project-config-store.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nfunction packageRootFromScript(): string {\n // `dist/sync-workflow.js` → package root (contains `assets/`)\n return path.resolve(__dirname, \"..\");\n}\n\nfunction isRenderedAgentEntry(\n entry: AssetEntry,\n): entry is AssetEntry & { renderAgent: NonNullable<AssetEntry[\"renderAgent\"]> } {\n return entry.renderAgent !== undefined;\n}\n\nfunction main(): void {\n const cwd = process.cwd();\n const packageRoot = packageRootFromScript();\n const config = normalizeProjectConfig(\n loadProjectConfig(cwd) ?? createDefaultProjectConfig(),\n );\n\n for (const entry of DEFAULT_MANIFEST) {\n const src = path.join(packageRoot, entry.source);\n const dest = path.join(cwd, \".cursor\", entry.target);\n mkdirSync(path.dirname(dest), { recursive: true });\n if (isRenderedAgentEntry(entry)) {\n const tpl = readFileSync(src, \"utf8\");\n const model = config.workflow.models[entry.renderAgent];\n writeFileSync(dest, renderAgentTemplate(tpl, model), \"utf8\");\n } else {\n cpSync(src, dest);\n }\n }\n\n console.log(\n \"Synced workflow assets into .cursor/ (agents rendered from templates; other files copied).\",\n );\n}\n\nmain();\n","export const PROJECT_CONFIG_FILENAME = \"workflow.json\";\n\nexport type BacklogProviderKind = \"file\" | \"github-issues\";\nexport type WorkflowDefaultMode = \"required\" | \"optional\";\n\n/** Subagent roles with configurable Cursor model ids (see `assets/agents/*.md`). */\nexport type WorkflowModelRole =\n | \"planner\"\n | \"architect\"\n | \"developer\"\n | \"tester\";\n\nexport interface WorkflowModels {\n readonly planner: string;\n readonly architect: string;\n readonly developer: string;\n readonly tester: string;\n}\n\n/**\n * GitHub backlog mode uses provider key `github-issues` (legacy name) and is\n * GitHub Project v2–first: issues are ordered by the Project **Priority** field,\n * workflow state by the Project **Status** field, and **milestones** represent epics.\n */\nexport interface GitHubIssuesBacklogConfig {\n readonly repository: string;\n /** GitHub Project (v2) number as shown on the repository Projects tab. */\n readonly projectNumber: number;\n /**\n * Organization or user login that owns the project. When omitted, tooling\n * assumes the repository owner (the segment before `/` in `repository`).\n */\n readonly projectOwner?: string;\n /** Project field name used for backlog ordering (default `Priority`). */\n readonly priorityField: string;\n /** Project field name used for workflow status (default `Status`). */\n readonly statusField: string;\n /**\n * Optional issue label filter (secondary to Project membership). Omit or leave\n * empty when everything is driven from the Project.\n */\n readonly label?: string;\n readonly mcpServerName: string;\n}\n\nexport interface ProjectConfig {\n readonly backlog: {\n readonly provider: BacklogProviderKind;\n readonly file?: {\n readonly path: string;\n };\n readonly \"github-issues\"?: GitHubIssuesBacklogConfig;\n };\n readonly workflow: {\n readonly defaults: {\n readonly architectReview: WorkflowDefaultMode;\n readonly testing: WorkflowDefaultMode;\n };\n readonly models: WorkflowModels;\n };\n}\n\n/** Defaults match pre-template agent frontmatter in `assets/agents/`. */\nexport function createDefaultWorkflowModels(): WorkflowModels {\n return {\n planner: \"gpt-5.4-high\",\n architect: \"gpt-5.4-high\",\n developer: \"composer-2-fast\",\n tester: \"composer-2-fast\",\n };\n}\n\nexport function createDefaultProjectConfig(): ProjectConfig {\n return {\n backlog: {\n provider: \"file\",\n file: {\n path: \"docs/backlog.md\",\n },\n },\n workflow: {\n defaults: {\n architectReview: \"required\",\n testing: \"required\",\n },\n models: createDefaultWorkflowModels(),\n },\n };\n}\n\nexport const DEFAULT_PROJECT_CONFIG = createDefaultProjectConfig();\n\nfunction isNonEmptyString(v: unknown): v is string {\n return typeof v === \"string\" && v.trim().length > 0;\n}\n\nfunction normalizeBacklog(\n raw: unknown,\n base: ProjectConfig[\"backlog\"],\n): ProjectConfig[\"backlog\"] {\n if (!raw || typeof raw !== \"object\") {\n return base;\n }\n const b = raw as Record<string, unknown>;\n const provider =\n b.provider === \"github-issues\" || b.provider === \"file\"\n ? b.provider\n : base.provider;\n\n if (provider === \"file\") {\n const fileRaw = b.file;\n const pathFrom =\n fileRaw && typeof fileRaw === \"object\" &&\n isNonEmptyString((fileRaw as Record<string, unknown>).path)\n ? String((fileRaw as Record<string, unknown>).path).trim()\n : base.file!.path;\n return { provider: \"file\", file: { path: pathFrom } };\n }\n\n const ghRaw = b[\"github-issues\"];\n const githubOptionalDefaults = {\n priorityField: \"Priority\",\n statusField: \"Status\",\n mcpServerName: \"github\",\n } as const;\n\n function parsePositiveInt(v: unknown): number | undefined {\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) {\n return Math.trunc(v);\n }\n if (isNonEmptyString(v)) {\n const n = Number.parseInt(String(v).trim(), 10);\n if (Number.isFinite(n) && n > 0) {\n return n;\n }\n }\n return undefined;\n }\n\n /**\n * GitHub mode requires an explicit repository and project number. Missing or\n * invalid nested config falls back to the default file backlog so we never\n * invent a plausible-looking `owner/repo` + `projectNumber: 1` target.\n */\n if (ghRaw && typeof ghRaw === \"object\") {\n const g = ghRaw as Record<string, unknown>;\n const repository = isNonEmptyString(g.repository)\n ? g.repository.trim()\n : undefined;\n const projectNumber = parsePositiveInt(g.projectNumber);\n\n if (repository !== undefined && projectNumber !== undefined) {\n const priorityField = isNonEmptyString(g.priorityField)\n ? g.priorityField.trim()\n : githubOptionalDefaults.priorityField;\n const statusField = isNonEmptyString(g.statusField)\n ? g.statusField.trim()\n : githubOptionalDefaults.statusField;\n const labelRaw = g.label;\n const label =\n isNonEmptyString(labelRaw) ? labelRaw.trim() : undefined;\n\n return {\n provider: \"github-issues\",\n \"github-issues\": {\n repository,\n projectNumber,\n projectOwner: isNonEmptyString(g.projectOwner)\n ? g.projectOwner.trim()\n : undefined,\n priorityField,\n statusField,\n ...(label !== undefined ? { label } : {}),\n mcpServerName: isNonEmptyString(g.mcpServerName)\n ? g.mcpServerName.trim()\n : githubOptionalDefaults.mcpServerName,\n },\n };\n }\n }\n\n return base;\n}\n\nfunction normalizeWorkflow(\n raw: unknown,\n base: ProjectConfig[\"workflow\"],\n): ProjectConfig[\"workflow\"] {\n const models = { ...base.models };\n let architectReview = base.defaults.architectReview;\n let testing = base.defaults.testing;\n\n if (raw && typeof raw === \"object\") {\n const w = raw as Record<string, unknown>;\n const d = w.defaults;\n if (d && typeof d === \"object\") {\n const def = d as Record<string, unknown>;\n if (def.architectReview === \"required\" || def.architectReview === \"optional\") {\n architectReview = def.architectReview;\n }\n if (def.testing === \"required\" || def.testing === \"optional\") {\n testing = def.testing;\n }\n }\n const m = w.models;\n if (m && typeof m === \"object\") {\n const mo = m as Record<string, unknown>;\n const pick = (key: keyof WorkflowModels): string => {\n const v = mo[key];\n return isNonEmptyString(v) ? v.trim() : base.models[key];\n };\n models.planner = pick(\"planner\");\n models.architect = pick(\"architect\");\n models.developer = pick(\"developer\");\n models.tester = pick(\"tester\");\n }\n }\n\n return {\n defaults: { architectReview, testing },\n models,\n };\n}\n\n/**\n * Merges parsed JSON with defaults so older configs without `workflow.models`\n * (or partial fields) remain valid.\n */\nexport function normalizeProjectConfig(raw: unknown): ProjectConfig {\n const base = createDefaultProjectConfig();\n if (!raw || typeof raw !== \"object\") {\n return base;\n }\n\n const o = raw as Record<string, unknown>;\n return {\n backlog: normalizeBacklog(o.backlog, base.backlog),\n workflow: normalizeWorkflow(o.workflow, base.workflow),\n };\n}\n","import type { WorkflowModelRole } from \"./config.js\";\n\nexport interface AssetEntry {\n readonly source: string;\n readonly target: string;\n /**\n * When set, `source` is a UTF-8 template with `{{MODEL}}`; install/sync\n * substitutes the value from `workflow.models.<role>`.\n */\n readonly renderAgent?: WorkflowModelRole;\n}\n\n/** Maps each shipped file under `assets/` to its path under `<cwd>/.cursor/`. */\nexport const DEFAULT_MANIFEST = [\n {\n source: \"assets/agents/architect.md\",\n target: \"agents/architect.md\",\n renderAgent: \"architect\",\n },\n {\n source: \"assets/agents/developer.md\",\n target: \"agents/developer.md\",\n renderAgent: \"developer\",\n },\n {\n source: \"assets/agents/planner.md\",\n target: \"agents/planner.md\",\n renderAgent: \"planner\",\n },\n {\n source: \"assets/agents/tester.md\",\n target: \"agents/tester.md\",\n renderAgent: \"tester\",\n },\n { source: \"assets/rules/global.mdc\", target: \"rules/global.mdc\" },\n { source: \"assets/rules/init.mdc\", target: \"rules/init.mdc\" },\n { source: \"assets/templates/backlog.md\", target: \"templates/backlog.md\" },\n { source: \"assets/templates/design.md\", target: \"templates/design.md\" },\n { source: \"assets/templates/overview.md\", target: \"templates/overview.md\" },\n {\n source: \"assets/templates/testability/README.md\",\n target: \"templates/testability/README.md\",\n },\n] as const satisfies readonly AssetEntry[];\n","/** Placeholder in shipped agent templates; replaced at init/sync time. */\nexport const AGENT_MODEL_PLACEHOLDER = \"{{MODEL}}\";\n\nexport function renderAgentTemplate(\n templateUtf8: string,\n model: string,\n): string {\n if (!templateUtf8.includes(AGENT_MODEL_PLACEHOLDER)) {\n throw new Error(\n `Agent template is missing ${AGENT_MODEL_PLACEHOLDER} placeholder`,\n );\n }\n return templateUtf8.replaceAll(AGENT_MODEL_PLACEHOLDER, model);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport {\n DEFAULT_PROJECT_CONFIG,\n normalizeProjectConfig,\n PROJECT_CONFIG_FILENAME,\n type ProjectConfig,\n} from \"../domain/config.js\";\n\nconst LEGACY_PROJECT_CONFIG_FILENAME = \"byrde.json\";\n\nexport interface ProjectConfigWriteResult {\n readonly path: string;\n readonly created: boolean;\n}\n\nexport function projectConfigPath(cwd: string): string {\n return path.join(cwd, \".cursor\", PROJECT_CONFIG_FILENAME);\n}\n\nexport function loadProjectConfig(cwd: string): ProjectConfig | undefined {\n const configPath = projectConfigPath(cwd);\n const legacyPath = path.join(cwd, \".cursor\", LEGACY_PROJECT_CONFIG_FILENAME);\n\n if (existsSync(configPath)) {\n return normalizeProjectConfig(\n JSON.parse(readFileSync(configPath, \"utf8\")),\n );\n }\n\n if (existsSync(legacyPath)) {\n return normalizeProjectConfig(\n JSON.parse(readFileSync(legacyPath, \"utf8\")),\n );\n }\n\n return undefined;\n}\n\nexport function ensureProjectConfig(\n cwd: string,\n config: ProjectConfig = DEFAULT_PROJECT_CONFIG,\n): ProjectConfigWriteResult {\n const configPath = projectConfigPath(cwd);\n if (existsSync(configPath)) {\n return {\n path: configPath,\n created: false,\n };\n }\n\n mkdirSync(path.dirname(configPath), { recursive: true });\n const normalized = normalizeProjectConfig(config);\n writeFileSync(configPath, `${JSON.stringify(normalized, null, 2)}\\n`, \"utf8\");\n return {\n path: configPath,\n created: true,\n };\n}\n\nexport function saveProjectConfig(\n cwd: string,\n config: ProjectConfig = DEFAULT_PROJECT_CONFIG,\n): ProjectConfigWriteResult {\n const configPath = projectConfigPath(cwd);\n const created = !existsSync(configPath);\n mkdirSync(path.dirname(configPath), { recursive: true });\n const normalized = normalizeProjectConfig(config);\n writeFileSync(configPath, `${JSON.stringify(normalized, null, 2)}\\n`, \"utf8\");\n return {\n path: configPath,\n created,\n };\n}\n"],"mappings":"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","names":["mkdirSync","readFileSync","writeFileSync","path","path","mkdirSync","readFileSync","writeFileSync"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@byrde/cursor",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Byrde Cursor workflow CLI and assets",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"byrde-cursor": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/",
|
|
17
|
+
"assets/"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"prepack": "npm run build",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"sync": "npm run build && node dist/sync-workflow.js"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"cursor",
|
|
35
|
+
"byrde",
|
|
36
|
+
"cli"
|
|
37
|
+
],
|
|
38
|
+
"license": "UNLICENSED",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/Byrde/.cursor.git"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^25.5.0",
|
|
45
|
+
"tsup": "^8.5.1",
|
|
46
|
+
"typescript": "^6.0.2",
|
|
47
|
+
"vitest": "^4.1.1"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@inquirer/prompts": "^8.3.2",
|
|
51
|
+
"commander": "^14.0.3"
|
|
52
|
+
}
|
|
53
|
+
}
|