planmode 0.1.0 → 0.1.2

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/dist/index.js CHANGED
@@ -57,7 +57,7 @@ function setGitHubToken(token) {
57
57
  function getRegistries() {
58
58
  const config = readConfig();
59
59
  return {
60
- default: "github.com/planmode/registry",
60
+ default: "github.com/kaihannonen/planmode.org/registry",
61
61
  ...config.registries
62
62
  };
63
63
  }
@@ -76,11 +76,12 @@ function getHeaders() {
76
76
  return headers;
77
77
  }
78
78
  function registryRawUrl(registryUrl, filePath) {
79
- const match = registryUrl.match(/^github\.com\/([^/]+)\/([^/]+)$/);
79
+ const match = registryUrl.match(/^github\.com\/([^/]+)\/([^/]+)(?:\/(.+))?$/);
80
80
  if (!match) {
81
81
  throw new Error(`Invalid registry URL: ${registryUrl}`);
82
82
  }
83
- return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/main/${filePath}`;
83
+ const subpath = match[3] ? `${match[3]}/` : "";
84
+ return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/main/${subpath}${filePath}`;
84
85
  }
85
86
  function resolveRegistry(packageName) {
86
87
  const registries = getRegistries();
@@ -643,10 +644,11 @@ async function installPackage(packageName, options = {}) {
643
644
  const { version, metadata } = await resolveVersion(packageName, options.version);
644
645
  const versionMeta = await fetchVersionMetadata(packageName, version);
645
646
  logger.info(`Fetching ${packageName}@${version}...`);
647
+ const basePath = versionMeta.source.path ? `${versionMeta.source.path}/` : "";
646
648
  const manifestRaw = await fetchFileAtTag(
647
649
  versionMeta.source.repository,
648
650
  versionMeta.source.tag,
649
- "planmode.yaml"
651
+ `${basePath}planmode.yaml`
650
652
  );
651
653
  const manifest = parseManifest(manifestRaw);
652
654
  let content;
@@ -656,7 +658,7 @@ async function installPackage(packageName, options = {}) {
656
658
  content = await fetchFileAtTag(
657
659
  versionMeta.source.repository,
658
660
  versionMeta.source.tag,
659
- manifest.content_file
661
+ `${basePath}${manifest.content_file}`
660
662
  );
661
663
  } else {
662
664
  throw new Error("Package has no content or content_file");
@@ -751,14 +753,27 @@ async function updatePackage(packageName, projectDir = process.cwd()) {
751
753
  }
752
754
 
753
755
  // src/commands/install.ts
754
- var installCommand = new Command("install").description("Install a package into the current project").argument("<package>", "Package name (e.g., nextjs-tailwind-starter)").option("-v, --version <version>", "Install specific version").option("--rule", "Force install as a rule to .claude/rules/").option("--no-input", "Fail if any required variable is missing").action(
756
+ function parseVariables(pairs) {
757
+ const vars = {};
758
+ for (const pair of pairs) {
759
+ const eq = pair.indexOf("=");
760
+ if (eq === -1) {
761
+ throw new Error(`Invalid variable format: "${pair}". Use --set key=value`);
762
+ }
763
+ vars[pair.slice(0, eq)] = pair.slice(eq + 1);
764
+ }
765
+ return vars;
766
+ }
767
+ var installCommand = new Command("install").description("Install a package into the current project").argument("<package>", "Package name (e.g., nextjs-tailwind-starter)").option("-v, --version <version>", "Install specific version").option("--rule", "Force install as a rule to .claude/rules/").option("--no-input", "Fail if any required variable is missing").option("--set <key=value...>", "Set template variables (e.g., --set project_name=myapp)").action(
755
768
  async (packageName, options) => {
756
769
  try {
757
770
  logger.blank();
771
+ const variables = options.set ? parseVariables(options.set) : void 0;
758
772
  await installPackage(packageName, {
759
773
  version: options.version,
760
774
  forceRule: options.rule,
761
- noInput: options.input === false
775
+ noInput: options.input === false,
776
+ variables
762
777
  });
763
778
  logger.blank();
764
779
  } catch (err) {
@@ -1279,7 +1294,7 @@ var loginCommand = new Command10("login").description("Configure GitHub authenti
1279
1294
 
1280
1295
  // src/index.ts
1281
1296
  var program = new Command11();
1282
- program.name("planmode").description("The open source package manager for AI plans, rules, and prompts.").version("0.1.0");
1297
+ program.name("planmode").description("The open source package manager for AI plans, rules, and prompts.").version("0.1.1");
1283
1298
  program.addCommand(installCommand);
1284
1299
  program.addCommand(uninstallCommand);
1285
1300
  program.addCommand(searchCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planmode",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The open source package manager for AI plans, rules, and prompts.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,23 +2,38 @@ import { Command } from "commander";
2
2
  import { installPackage } from "../lib/installer.js";
3
3
  import { logger } from "../lib/logger.js";
4
4
 
5
+ function parseVariables(pairs: string[]): Record<string, string> {
6
+ const vars: Record<string, string> = {};
7
+ for (const pair of pairs) {
8
+ const eq = pair.indexOf("=");
9
+ if (eq === -1) {
10
+ throw new Error(`Invalid variable format: "${pair}". Use --set key=value`);
11
+ }
12
+ vars[pair.slice(0, eq)] = pair.slice(eq + 1);
13
+ }
14
+ return vars;
15
+ }
16
+
5
17
  export const installCommand = new Command("install")
6
18
  .description("Install a package into the current project")
7
19
  .argument("<package>", "Package name (e.g., nextjs-tailwind-starter)")
8
20
  .option("-v, --version <version>", "Install specific version")
9
21
  .option("--rule", "Force install as a rule to .claude/rules/")
10
22
  .option("--no-input", "Fail if any required variable is missing")
23
+ .option("--set <key=value...>", "Set template variables (e.g., --set project_name=myapp)")
11
24
  .action(
12
25
  async (
13
26
  packageName: string,
14
- options: { version?: string; rule?: boolean; input?: boolean },
27
+ options: { version?: string; rule?: boolean; input?: boolean; set?: string[] },
15
28
  ) => {
16
29
  try {
17
30
  logger.blank();
31
+ const variables = options.set ? parseVariables(options.set) : undefined;
18
32
  await installPackage(packageName, {
19
33
  version: options.version,
20
34
  forceRule: options.rule,
21
35
  noInput: options.input === false,
36
+ variables,
22
37
  });
23
38
  logger.blank();
24
39
  } catch (err) {
package/src/index.ts CHANGED
@@ -15,7 +15,7 @@ const program = new Command();
15
15
  program
16
16
  .name("planmode")
17
17
  .description("The open source package manager for AI plans, rules, and prompts.")
18
- .version("0.1.0");
18
+ .version("0.1.1");
19
19
 
20
20
  program.addCommand(installCommand);
21
21
  program.addCommand(uninstallCommand);
package/src/lib/config.ts CHANGED
@@ -52,7 +52,7 @@ export function setGitHubToken(token: string): void {
52
52
  export function getRegistries(): Record<string, string> {
53
53
  const config = readConfig();
54
54
  return {
55
- default: "github.com/planmode/registry",
55
+ default: "github.com/kaihannonen/planmode.org/registry",
56
56
  ...config.registries,
57
57
  };
58
58
  }
@@ -60,10 +60,11 @@ export async function installPackage(
60
60
 
61
61
  // Fetch manifest
62
62
  logger.info(`Fetching ${packageName}@${version}...`);
63
+ const basePath = versionMeta.source.path ? `${versionMeta.source.path}/` : "";
63
64
  const manifestRaw = await fetchFileAtTag(
64
65
  versionMeta.source.repository,
65
66
  versionMeta.source.tag,
66
- "planmode.yaml",
67
+ `${basePath}planmode.yaml`,
67
68
  );
68
69
  const manifest = parseManifest(manifestRaw);
69
70
 
@@ -75,7 +76,7 @@ export async function installPackage(
75
76
  content = await fetchFileAtTag(
76
77
  versionMeta.source.repository,
77
78
  versionMeta.source.tag,
78
- manifest.content_file,
79
+ `${basePath}${manifest.content_file}`,
79
80
  );
80
81
  } else {
81
82
  throw new Error("Package has no content or content_file");
@@ -18,12 +18,13 @@ function getHeaders(): Record<string, string> {
18
18
  }
19
19
 
20
20
  function registryRawUrl(registryUrl: string, filePath: string): string {
21
- // Convert github.com/org/repo to raw.githubusercontent.com URL
22
- const match = registryUrl.match(/^github\.com\/([^/]+)\/([^/]+)$/);
21
+ // Convert github.com/org/repo[/subpath] to raw.githubusercontent.com URL
22
+ const match = registryUrl.match(/^github\.com\/([^/]+)\/([^/]+)(?:\/(.+))?$/);
23
23
  if (!match) {
24
24
  throw new Error(`Invalid registry URL: ${registryUrl}`);
25
25
  }
26
- return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/main/${filePath}`;
26
+ const subpath = match[3] ? `${match[3]}/` : "";
27
+ return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/main/${subpath}${filePath}`;
27
28
  }
28
29
 
29
30
  function resolveRegistry(packageName: string): string {
@@ -95,6 +95,7 @@ export interface VersionMetadata {
95
95
  repository: string;
96
96
  tag: string;
97
97
  sha: string;
98
+ path?: string;
98
99
  };
99
100
  files: string[];
100
101
  content_hash: string;