codebyplan 1.1.0 → 1.3.0

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.
Files changed (2) hide show
  1. package/dist/cli.js +113 -8
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ var VERSION, PACKAGE_NAME;
14
14
  var init_version = __esm({
15
15
  "src/lib/version.ts"() {
16
16
  "use strict";
17
- VERSION = "1.1.0";
17
+ VERSION = "1.3.0";
18
18
  PACKAGE_NAME = "codebyplan";
19
19
  }
20
20
  });
@@ -296,21 +296,45 @@ var init_hook_registry = __esm({
296
296
  });
297
297
 
298
298
  // src/lib/variables.ts
299
+ function splitFrontmatter(content) {
300
+ const fmMatch = content.match(/^(---\s*\n[\s\S]*?\n---\n?)([\s\S]*)$/);
301
+ if (fmMatch) {
302
+ return { frontmatter: fmMatch[1], body: fmMatch[2] };
303
+ }
304
+ if (content.startsWith("#!/") || content.startsWith("# @")) {
305
+ const lines = content.split("\n");
306
+ let headerEnd = 0;
307
+ for (let i = 0; i < lines.length; i++) {
308
+ if (lines[i].startsWith("#") || lines[i].startsWith("#!/") || lines[i].trim() === "") {
309
+ headerEnd = i + 1;
310
+ } else {
311
+ break;
312
+ }
313
+ }
314
+ return {
315
+ frontmatter: lines.slice(0, headerEnd).join("\n") + "\n",
316
+ body: lines.slice(headerEnd).join("\n")
317
+ };
318
+ }
319
+ return { frontmatter: "", body: content };
320
+ }
299
321
  function substituteVariables(content, repoData) {
300
322
  if (!content.includes("{{")) return content;
301
- let result = content;
323
+ const { frontmatter, body } = splitFrontmatter(content);
324
+ let result = body;
302
325
  for (const [name, resolver] of Object.entries(TEMPLATE_VARIABLES)) {
303
326
  const placeholder = `{{${name}}}`;
304
327
  if (result.includes(placeholder)) {
305
328
  result = result.replaceAll(placeholder, resolver(repoData));
306
329
  }
307
330
  }
308
- return result;
331
+ return frontmatter + result;
309
332
  }
310
333
  function escapeRegex(str) {
311
334
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
312
335
  }
313
336
  function reverseSubstituteVariables(content, repoData) {
337
+ const { frontmatter, body } = splitFrontmatter(content);
314
338
  const entries = [];
315
339
  for (const [name, resolver] of Object.entries(TEMPLATE_VARIABLES)) {
316
340
  const value = resolver(repoData);
@@ -318,7 +342,7 @@ function reverseSubstituteVariables(content, repoData) {
318
342
  entries.push([value, `{{${name}}}`]);
319
343
  }
320
344
  entries.sort((a, b) => b[0].length - a[0].length);
321
- let result = content;
345
+ let result = body;
322
346
  for (const [value, placeholder] of entries) {
323
347
  if (value.length < 8) {
324
348
  const pattern = new RegExp(`\\b${escapeRegex(value)}\\b`, "g");
@@ -327,7 +351,7 @@ function reverseSubstituteVariables(content, repoData) {
327
351
  result = result.replaceAll(value, placeholder);
328
352
  }
329
353
  }
330
- return result;
354
+ return frontmatter + result;
331
355
  }
332
356
  var TEMPLATE_VARIABLES;
333
357
  var init_variables = __esm({
@@ -1038,6 +1062,13 @@ async function scanLocalFiles(claudeDir, projectPath) {
1038
1062
  await scanFlatType(join5(claudeDir, "rules"), "rule", ".md", result);
1039
1063
  await scanFlatType(join5(claudeDir, "hooks"), "hook", ".sh", result);
1040
1064
  await scanTemplates(join5(claudeDir, "templates"), result);
1065
+ await scanCategorizedType(
1066
+ join5(claudeDir, "context"),
1067
+ "context",
1068
+ ".md",
1069
+ result
1070
+ );
1071
+ await scanDocsRecursive(join5(claudeDir, "docs"), result);
1041
1072
  await scanSettings(claudeDir, projectPath, result);
1042
1073
  return result;
1043
1074
  }
@@ -1112,6 +1143,69 @@ async function scanFlatType(dir, type, ext, result) {
1112
1143
  }
1113
1144
  }
1114
1145
  }
1146
+ async function scanCategorizedType(dir, type, ext, result) {
1147
+ let entries;
1148
+ try {
1149
+ entries = await readdir3(dir, { withFileTypes: true });
1150
+ } catch {
1151
+ return;
1152
+ }
1153
+ for (const entry of entries) {
1154
+ if (entry.isDirectory()) {
1155
+ const category = entry.name;
1156
+ let subEntries;
1157
+ try {
1158
+ subEntries = await readdir3(join5(dir, category), {
1159
+ withFileTypes: true
1160
+ });
1161
+ } catch {
1162
+ continue;
1163
+ }
1164
+ for (const sub of subEntries) {
1165
+ if (sub.isFile() && sub.name.endsWith(ext)) {
1166
+ const name = sub.name.slice(0, -ext.length);
1167
+ const content = await readFile5(
1168
+ join5(dir, category, sub.name),
1169
+ "utf-8"
1170
+ );
1171
+ const scope = extractScope(content, type);
1172
+ const key = compositeKey(type, name, category);
1173
+ result.set(key, { type, name, category, content, scope });
1174
+ }
1175
+ }
1176
+ } else if (entry.isFile() && entry.name.endsWith(ext)) {
1177
+ const name = entry.name.slice(0, -ext.length);
1178
+ const content = await readFile5(join5(dir, entry.name), "utf-8");
1179
+ const scope = extractScope(content, type);
1180
+ const key = compositeKey(type, name, null);
1181
+ result.set(key, { type, name, category: null, content, scope });
1182
+ }
1183
+ }
1184
+ }
1185
+ async function scanDocsRecursive(docsDir, result) {
1186
+ await scanDocsDir(docsDir, docsDir, result);
1187
+ }
1188
+ async function scanDocsDir(baseDir, currentDir, result) {
1189
+ let entries;
1190
+ try {
1191
+ entries = await readdir3(currentDir, { withFileTypes: true });
1192
+ } catch {
1193
+ return;
1194
+ }
1195
+ for (const entry of entries) {
1196
+ if (entry.isDirectory()) {
1197
+ await scanDocsDir(baseDir, join5(currentDir, entry.name), result);
1198
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
1199
+ const name = entry.name.slice(0, -3);
1200
+ const content = await readFile5(join5(currentDir, entry.name), "utf-8");
1201
+ const scope = extractScope(content, "docs");
1202
+ const relDir = currentDir.slice(baseDir.length + 1);
1203
+ const category = relDir || null;
1204
+ const key = compositeKey("docs", name, category);
1205
+ result.set(key, { type: "docs", name, category, content, scope });
1206
+ }
1207
+ }
1208
+ }
1115
1209
  async function scanTemplates(dir, result) {
1116
1210
  let entries;
1117
1211
  try {
@@ -2667,7 +2761,10 @@ function groupByType(items) {
2667
2761
  rule: "Rules",
2668
2762
  hook: "Hooks",
2669
2763
  template: "Templates",
2670
- settings: "Settings"
2764
+ settings: "Settings",
2765
+ context: "Context",
2766
+ docs_stack: "Stack Docs",
2767
+ docs: "Docs"
2671
2768
  };
2672
2769
  for (const item of items) {
2673
2770
  const label = typeLabels[item.type] ?? item.type;
@@ -2685,6 +2782,9 @@ function getLocalFilePath(claudeDir, projectPath, remote) {
2685
2782
  rule: { dir: "rules", ext: ".md" },
2686
2783
  hook: { dir: "hooks", ext: ".sh" },
2687
2784
  template: { dir: "templates", ext: "" },
2785
+ context: { dir: "context", ext: ".md" },
2786
+ docs_stack: { dir: join7("docs", "stack"), ext: ".md" },
2787
+ docs: { dir: "docs", ext: ".md" },
2688
2788
  claude_md: { dir: "", ext: "" },
2689
2789
  settings: { dir: "", ext: "" }
2690
2790
  };
@@ -2698,11 +2798,13 @@ function getLocalFilePath(claudeDir, projectPath, remote) {
2698
2798
  if (remote.type === "command" && remote.category)
2699
2799
  return join7(typeDir, remote.category, `${remote.name}${cfg.ext}`);
2700
2800
  if (remote.type === "template") return join7(typeDir, remote.name);
2801
+ if (remote.category && (remote.type === "context" || remote.type === "docs_stack" || remote.type === "docs"))
2802
+ return join7(typeDir, remote.category, `${remote.name}${cfg.ext}`);
2701
2803
  return join7(typeDir, `${remote.name}${cfg.ext}`);
2702
2804
  }
2703
2805
  function getSyncVersion() {
2704
2806
  try {
2705
- return "1.1.0";
2807
+ return "1.3.0";
2706
2808
  } catch {
2707
2809
  return "unknown";
2708
2810
  }
@@ -2716,7 +2818,10 @@ function flattenSyncData(data) {
2716
2818
  rules: "rule",
2717
2819
  hooks: "hook",
2718
2820
  templates: "template",
2719
- settings: "settings"
2821
+ settings: "settings",
2822
+ contexts: "context",
2823
+ docs_stack: "docs_stack",
2824
+ docs: "docs"
2720
2825
  };
2721
2826
  for (const [syncKey, typeName] of Object.entries(typeMap)) {
2722
2827
  const files = data[syncKey] ?? [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebyplan",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "CLI for CodeByPlan — AI-powered development planning and tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "repository": {
34
34
  "type": "git",
35
35
  "url": "https://github.com/codebyplan/codebyplan.git",
36
- "directory": "packages/codebyplan"
36
+ "directory": "packages/codebyplan-package"
37
37
  },
38
38
  "license": "MIT",
39
39
  "publishConfig": {