pre-claude 0.0.1-beta.4 → 0.0.1-beta.5

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 CHANGED
@@ -2,14 +2,13 @@
2
2
 
3
3
  [日本語版](./README-ja.md)
4
4
 
5
- **Pre** Claude is a tool for creating prompts via TUI when running Claude.
6
- You can define forms in TypeScript config files and share them with your team.
5
+ A TUI tool for efficiently creating complex prompts with structured forms for Claude Code.
6
+ Define templates in TypeScript config files to share with your team and enable reproducible prompt workflows.
7
+ Works seamlessly with your existing Claude Code setup including MCP and Skills.
7
8
 
8
- ## Features
9
-
10
- - TypeScript-based form configuration
11
- - Interactive TUI form wizard
12
- - Uses local Claude Code settings
9
+ | Scenario Selection | Form Input | Preview |
10
+ |:---:|:---:|:---:|
11
+ | ![select](docs/assets/select.gif) | ![edit](docs/assets/edit.gif) | ![preview](docs/assets/preview.gif) |
13
12
 
14
13
  ## Requirements
15
14
 
package/dist/cli.js CHANGED
@@ -1,118 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { defineCommand as defineCommand3, runMain } from "citty";
4
+ import { defineCommand as defineCommand4, runMain } from "citty";
5
5
 
6
6
  // package.json
7
7
  var name = "pre-claude";
8
- var version = "0.0.1-beta.4";
8
+ var version = "0.0.1-beta.5";
9
9
  var description = "\u{1F40D} TUI for building structured prompts for Claude";
10
10
 
11
- // src/cli/commands/init.ts
12
- import * as fs from "node:fs";
11
+ // src/cli/commands/example.tsx
13
12
  import * as path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
14
  import { defineCommand } from "citty";
15
15
  import { consola } from "consola";
16
- var CONFIG_TEMPLATE = `// For more detailed configuration examples, see:
17
- // https://github.com/cut0/pre-claude/blob/main/examples
18
-
19
- import { defineConfig, defineScenario } from 'pre-claude';
20
-
21
- export default defineConfig({
22
- scenarios: [
23
- defineScenario({
24
- id: 'default',
25
- name: 'Design Doc Generator',
26
- steps: [
27
- {
28
- slug: 'overview',
29
- title: 'Overview',
30
- description: 'Basic information about the feature',
31
- name: 'overview',
32
- fields: [
33
- {
34
- type: 'input',
35
- id: 'title',
36
- label: 'Title',
37
- description: 'Feature title',
38
- placeholder: 'Enter feature title',
39
- required: true,
40
- },
41
- {
42
- type: 'textarea',
43
- id: 'description',
44
- label: 'Description',
45
- description: 'Detailed description of the feature',
46
- placeholder: 'Describe the feature...',
47
- rows: 4,
48
- },
49
- {
50
- type: 'select',
51
- id: 'priority',
52
- label: 'Priority',
53
- description: 'Feature priority level',
54
- placeholder: 'Select priority',
55
- options: [
56
- { value: 'high', label: 'High' },
57
- { value: 'medium', label: 'Medium' },
58
- { value: 'low', label: 'Low' },
59
- ],
60
- },
61
- ],
62
- },
63
- ],
64
- filename: ({ timestamp }) => \`\${timestamp}.md\`,
65
- prompt: ({ formData, aiContext }) =>
66
- \`Generate a design doc based on the following input:
67
- \${JSON.stringify({ formData, aiContext }, null, 2)}\`,
68
- }),
69
- ],
70
- permissions: {
71
- allowSave: true,
72
- },
73
- });
74
- `;
75
- var initCommand = defineCommand({
76
- meta: {
77
- name: "init",
78
- description: "Create a new pre-claude.config.ts file"
79
- },
80
- args: {
81
- output: {
82
- type: "string",
83
- description: "Output file path",
84
- alias: "o",
85
- default: "pre-claude.config.ts"
86
- },
87
- force: {
88
- type: "boolean",
89
- description: "Overwrite existing file",
90
- alias: "f",
91
- default: false
92
- }
93
- },
94
- async run({ args }) {
95
- const outputPath = path.resolve(process.cwd(), args.output);
96
- if (fs.existsSync(outputPath) && !args.force) {
97
- consola.error(`File already exists: ${outputPath}`);
98
- consola.info("Use --force (-f) to overwrite");
99
- process.exit(1);
100
- }
101
- try {
102
- fs.writeFileSync(outputPath, CONFIG_TEMPLATE, "utf-8");
103
- consola.success(`Config file created: ${outputPath}`);
104
- } catch (error) {
105
- consola.error("Failed to create config file:", error);
106
- process.exit(1);
107
- }
108
- }
109
- });
110
-
111
- // src/cli/commands/run.tsx
112
- import * as fs2 from "node:fs";
113
- import * as path2 from "node:path";
114
- import { defineCommand as defineCommand2 } from "citty";
115
- import { consola as consola2 } from "consola";
116
16
  import { createJiti } from "jiti";
117
17
  import { render } from "ink";
118
18
 
@@ -992,8 +892,8 @@ import { Box as Box13, Text as Text14 } from "ink";
992
892
  import { useCallback as useCallback4, useMemo as useMemo3, useState as useState6 } from "react";
993
893
 
994
894
  // src/tui/features/form/services.ts
995
- var getValueByPath = (obj, path3) => {
996
- return path3.split(".").reduce((acc, key) => {
895
+ var getValueByPath = (obj, path4) => {
896
+ return path4.split(".").reduce((acc, key) => {
997
897
  if (acc == null || typeof acc !== "object") return void 0;
998
898
  return acc[key];
999
899
  }, obj);
@@ -1160,14 +1060,14 @@ var buildAiContext = (steps) => {
1160
1060
  };
1161
1061
 
1162
1062
  // src/tui/features/form/utils.ts
1163
- var getValueByPath2 = (obj, path3) => {
1164
- return path3.split(".").reduce((acc, key) => {
1063
+ var getValueByPath2 = (obj, path4) => {
1064
+ return path4.split(".").reduce((acc, key) => {
1165
1065
  if (acc == null || typeof acc !== "object") return void 0;
1166
1066
  return acc[key];
1167
1067
  }, obj);
1168
1068
  };
1169
- var setValueByPath = (obj, path3, value) => {
1170
- const keys = path3.split(".");
1069
+ var setValueByPath = (obj, path4, value) => {
1070
+ const keys = path4.split(".");
1171
1071
  const result = { ...obj };
1172
1072
  let current = result;
1173
1073
  for (let i = 0; i < keys.length - 1; i++) {
@@ -2221,12 +2121,12 @@ var ScenarioForm = ({
2221
2121
  return void 0;
2222
2122
  }, [validationError]);
2223
2123
  const updateValue = useCallback4(
2224
- (path3, value) => {
2225
- const keys = path3.split(".");
2124
+ (path4, value) => {
2125
+ const keys = path4.split(".");
2226
2126
  if (keys.length === 1) {
2227
- formState.updateFieldValue(currentStep.name, path3, value);
2127
+ formState.updateFieldValue(currentStep.name, path4, value);
2228
2128
  } else {
2229
- const newStepValues = setValueByPath(stepValues, path3, value);
2129
+ const newStepValues = setValueByPath(stepValues, path4, value);
2230
2130
  const rootKey = keys[0];
2231
2131
  formState.updateFieldValue(
2232
2132
  currentStep.name,
@@ -2238,15 +2138,15 @@ var ScenarioForm = ({
2238
2138
  [currentStep.name, stepValues, formState]
2239
2139
  );
2240
2140
  const addRepeatableItem = useCallback4(
2241
- (repeatable, path3) => {
2141
+ (repeatable, path4) => {
2242
2142
  const newItem = buildRepeatableItemDefaults(repeatable);
2243
- if (path3.includes(".")) {
2244
- const items = getValueByPath2(stepValues, path3) || [];
2245
- const newStepValues = setValueByPath(stepValues, path3, [
2143
+ if (path4.includes(".")) {
2144
+ const items = getValueByPath2(stepValues, path4) || [];
2145
+ const newStepValues = setValueByPath(stepValues, path4, [
2246
2146
  ...items,
2247
2147
  newItem
2248
2148
  ]);
2249
- const rootKey = path3.split(".")[0];
2149
+ const rootKey = path4.split(".")[0];
2250
2150
  formState.updateFieldValue(
2251
2151
  currentStep.name,
2252
2152
  rootKey,
@@ -2263,8 +2163,8 @@ var ScenarioForm = ({
2263
2163
  [currentStep.name, stepValues, formState]
2264
2164
  );
2265
2165
  const removeRepeatableItem = useCallback4(
2266
- (repeatable, index, path3) => {
2267
- const pathParts = path3.split(".");
2166
+ (repeatable, index, path4) => {
2167
+ const pathParts = path4.split(".");
2268
2168
  pathParts.pop();
2269
2169
  const repeatablePath = pathParts.join(".");
2270
2170
  const minCount = repeatable.minCount ?? 0;
@@ -2778,7 +2678,7 @@ var App = ({ config, initialScenarioId }) => {
2778
2678
  return /* @__PURE__ */ jsx18(Text16, { color: "red", children: "Unknown screen" });
2779
2679
  };
2780
2680
 
2781
- // src/cli/commands/run.tsx
2681
+ // src/cli/commands/example.tsx
2782
2682
  import { jsx as jsx19 } from "react/jsx-runtime";
2783
2683
  var enterAlternateScreen = () => {
2784
2684
  process.stdout.write("\x1B[?1049h");
@@ -2788,11 +2688,190 @@ var exitAlternateScreen = () => {
2788
2688
  process.stdout.write("\x1B[?1049l");
2789
2689
  };
2790
2690
  var loadConfig = async (configPath) => {
2791
- const absolutePath = path2.resolve(process.cwd(), configPath);
2691
+ const jiti = createJiti(import.meta.url);
2692
+ const configModule = await jiti.import(configPath);
2693
+ const config = configModule.default;
2694
+ const result = safeParseConfig(config);
2695
+ if (!result.success) {
2696
+ const issues = result.issues.map((issue) => {
2697
+ const pathStr = issue.path?.map((p) => p.key).join(".") ?? "";
2698
+ return ` - ${pathStr}: ${issue.message}`;
2699
+ });
2700
+ throw new Error(`Invalid config:
2701
+ ${issues.join("\n")}`);
2702
+ }
2703
+ return config;
2704
+ };
2705
+ var exampleCommand = defineCommand({
2706
+ meta: {
2707
+ name: "example",
2708
+ description: "Run pre-claude with an example config to try it out"
2709
+ },
2710
+ args: {
2711
+ lang: {
2712
+ type: "string",
2713
+ description: "Language for example config (en or ja)",
2714
+ alias: "l",
2715
+ default: "en"
2716
+ }
2717
+ },
2718
+ async run({ args }) {
2719
+ const lang = args.lang;
2720
+ if (lang !== "en" && lang !== "ja") {
2721
+ consola.error(`Invalid language: ${lang}. Use 'en' or 'ja'.`);
2722
+ process.exit(1);
2723
+ }
2724
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2725
+ const examplesDir = path.resolve(__dirname, "../../examples");
2726
+ const configFileName = lang === "ja" ? "pre-claude-ja.config.ts" : "pre-claude.config.ts";
2727
+ const configPath = path.join(examplesDir, configFileName);
2728
+ try {
2729
+ const config = await loadConfig(configPath);
2730
+ if (process.stdin.isTTY !== true) {
2731
+ throw new Error(
2732
+ "TUI requires an interactive terminal. Please run this command in a terminal that supports raw mode."
2733
+ );
2734
+ }
2735
+ consola.info(
2736
+ `Running example with ${lang === "ja" ? "Japanese" : "English"} config...`
2737
+ );
2738
+ enterAlternateScreen();
2739
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx19(App, { config }));
2740
+ await waitUntilExit();
2741
+ exitAlternateScreen();
2742
+ } catch (error) {
2743
+ exitAlternateScreen();
2744
+ if (error instanceof Error) {
2745
+ consola.error(error.message);
2746
+ } else {
2747
+ consola.error("Failed to run example:", error);
2748
+ }
2749
+ process.exit(1);
2750
+ }
2751
+ }
2752
+ });
2753
+
2754
+ // src/cli/commands/init.ts
2755
+ import * as fs from "node:fs";
2756
+ import * as path2 from "node:path";
2757
+ import { defineCommand as defineCommand2 } from "citty";
2758
+ import { consola as consola2 } from "consola";
2759
+ var CONFIG_TEMPLATE = `// For more detailed configuration examples, see:
2760
+ // https://github.com/cut0/pre-claude/blob/main/examples
2761
+
2762
+ import { defineConfig, defineScenario } from 'pre-claude';
2763
+
2764
+ export default defineConfig({
2765
+ scenarios: [
2766
+ defineScenario({
2767
+ id: 'default',
2768
+ name: 'Design Doc Generator',
2769
+ steps: [
2770
+ {
2771
+ slug: 'overview',
2772
+ title: 'Overview',
2773
+ description: 'Basic information about the feature',
2774
+ name: 'overview',
2775
+ fields: [
2776
+ {
2777
+ type: 'input',
2778
+ id: 'title',
2779
+ label: 'Title',
2780
+ description: 'Feature title',
2781
+ placeholder: 'Enter feature title',
2782
+ required: true,
2783
+ },
2784
+ {
2785
+ type: 'textarea',
2786
+ id: 'description',
2787
+ label: 'Description',
2788
+ description: 'Detailed description of the feature',
2789
+ placeholder: 'Describe the feature...',
2790
+ rows: 4,
2791
+ },
2792
+ {
2793
+ type: 'select',
2794
+ id: 'priority',
2795
+ label: 'Priority',
2796
+ description: 'Feature priority level',
2797
+ placeholder: 'Select priority',
2798
+ options: [
2799
+ { value: 'high', label: 'High' },
2800
+ { value: 'medium', label: 'Medium' },
2801
+ { value: 'low', label: 'Low' },
2802
+ ],
2803
+ },
2804
+ ],
2805
+ },
2806
+ ],
2807
+ filename: ({ timestamp }) => \`\${timestamp}.md\`,
2808
+ prompt: ({ formData, aiContext }) =>
2809
+ \`Generate a design doc based on the following input:
2810
+ \${JSON.stringify({ formData, aiContext }, null, 2)}\`,
2811
+ }),
2812
+ ],
2813
+ permissions: {
2814
+ allowSave: true,
2815
+ },
2816
+ });
2817
+ `;
2818
+ var initCommand = defineCommand2({
2819
+ meta: {
2820
+ name: "init",
2821
+ description: "Create a new pre-claude.config.ts file"
2822
+ },
2823
+ args: {
2824
+ output: {
2825
+ type: "string",
2826
+ description: "Output file path",
2827
+ alias: "o",
2828
+ default: "pre-claude.config.ts"
2829
+ },
2830
+ force: {
2831
+ type: "boolean",
2832
+ description: "Overwrite existing file",
2833
+ alias: "f",
2834
+ default: false
2835
+ }
2836
+ },
2837
+ async run({ args }) {
2838
+ const outputPath = path2.resolve(process.cwd(), args.output);
2839
+ if (fs.existsSync(outputPath) && !args.force) {
2840
+ consola2.error(`File already exists: ${outputPath}`);
2841
+ consola2.info("Use --force (-f) to overwrite");
2842
+ process.exit(1);
2843
+ }
2844
+ try {
2845
+ fs.writeFileSync(outputPath, CONFIG_TEMPLATE, "utf-8");
2846
+ consola2.success(`Config file created: ${outputPath}`);
2847
+ } catch (error) {
2848
+ consola2.error("Failed to create config file:", error);
2849
+ process.exit(1);
2850
+ }
2851
+ }
2852
+ });
2853
+
2854
+ // src/cli/commands/run.tsx
2855
+ import * as fs2 from "node:fs";
2856
+ import * as path3 from "node:path";
2857
+ import { defineCommand as defineCommand3 } from "citty";
2858
+ import { consola as consola3 } from "consola";
2859
+ import { createJiti as createJiti2 } from "jiti";
2860
+ import { render as render2 } from "ink";
2861
+ import { jsx as jsx20 } from "react/jsx-runtime";
2862
+ var enterAlternateScreen2 = () => {
2863
+ process.stdout.write("\x1B[?1049h");
2864
+ process.stdout.write("\x1B[H");
2865
+ };
2866
+ var exitAlternateScreen2 = () => {
2867
+ process.stdout.write("\x1B[?1049l");
2868
+ };
2869
+ var loadConfig2 = async (configPath) => {
2870
+ const absolutePath = path3.resolve(process.cwd(), configPath);
2792
2871
  if (!fs2.existsSync(absolutePath)) {
2793
2872
  throw new Error(`Config file not found: ${absolutePath}`);
2794
2873
  }
2795
- const jiti = createJiti(import.meta.url);
2874
+ const jiti = createJiti2(import.meta.url);
2796
2875
  const configModule = await jiti.import(absolutePath);
2797
2876
  const config = configModule.default;
2798
2877
  const result = safeParseConfig(config);
@@ -2806,7 +2885,7 @@ ${issues.join("\n")}`);
2806
2885
  }
2807
2886
  return config;
2808
2887
  };
2809
- var runCommand = defineCommand2({
2888
+ var runCommand = defineCommand3({
2810
2889
  meta: {
2811
2890
  name: "run",
2812
2891
  description: "Start the TUI to fill out forms and generate prompts"
@@ -2827,7 +2906,7 @@ var runCommand = defineCommand2({
2827
2906
  async run({ args }) {
2828
2907
  const configPath = args.config;
2829
2908
  try {
2830
- const config = await loadConfig(configPath);
2909
+ const config = await loadConfig2(configPath);
2831
2910
  if (args.scenario != null) {
2832
2911
  const scenario = config.scenarios.find((s) => s.id === args.scenario);
2833
2912
  if (scenario == null) {
@@ -2842,18 +2921,18 @@ var runCommand = defineCommand2({
2842
2921
  "TUI requires an interactive terminal. Please run this command in a terminal that supports raw mode."
2843
2922
  );
2844
2923
  }
2845
- enterAlternateScreen();
2846
- const { waitUntilExit } = render(
2847
- /* @__PURE__ */ jsx19(App, { config, initialScenarioId: args.scenario })
2924
+ enterAlternateScreen2();
2925
+ const { waitUntilExit } = render2(
2926
+ /* @__PURE__ */ jsx20(App, { config, initialScenarioId: args.scenario })
2848
2927
  );
2849
2928
  await waitUntilExit();
2850
- exitAlternateScreen();
2929
+ exitAlternateScreen2();
2851
2930
  } catch (error) {
2852
- exitAlternateScreen();
2931
+ exitAlternateScreen2();
2853
2932
  if (error instanceof Error) {
2854
- consola2.error(error.message);
2933
+ consola3.error(error.message);
2855
2934
  } else {
2856
- consola2.error("Failed to run TUI:", error);
2935
+ consola3.error("Failed to run TUI:", error);
2857
2936
  }
2858
2937
  process.exit(1);
2859
2938
  }
@@ -2861,7 +2940,7 @@ var runCommand = defineCommand2({
2861
2940
  });
2862
2941
 
2863
2942
  // src/cli/index.ts
2864
- var main = defineCommand3({
2943
+ var main = defineCommand4({
2865
2944
  meta: {
2866
2945
  name,
2867
2946
  version,
@@ -2869,7 +2948,8 @@ var main = defineCommand3({
2869
2948
  },
2870
2949
  subCommands: {
2871
2950
  run: runCommand,
2872
- init: initCommand
2951
+ init: initCommand,
2952
+ example: exampleCommand
2873
2953
  }
2874
2954
  });
2875
2955
  runMain(main);