thought-cabinet 0.2.0 → 0.2.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 CHANGED
@@ -21,30 +21,52 @@ Thought Cabinet solves these by providing:
21
21
  ## Quick Start
22
22
 
23
23
  ```bash
24
- cd your-project
25
-
26
24
  # 1. Install
27
25
  pnpm install -g thought-cabinet
28
26
 
29
- # 2. Initialize thoughts in your project
30
- thc init
31
-
32
- # 3. Install skills to your AI agent
27
+ # 2. Install skills to your AI agent
28
+ cd your-project
33
29
  thc skill install
34
30
 
35
- # 4. Use skills in your agent session (e.g. Claude Code)
31
+ # 3. Onboard the project (in your agent session)
32
+ > /onboard
33
+ ```
34
+
35
+ The `/onboard` skill initializes thoughts, creates agent memory (`AGENTS.md`), and installs git hooks — all in one step.
36
+
37
+ Once onboarded, use the workflow skills:
38
+
39
+ ```bash
36
40
  > /research-codebase How does the authentication system work?
37
41
  > /creating-plan Add OAuth2 support based on the research
38
42
  > /implementing-plan thoughts/shared/plans/add-oauth.md
39
43
  > /validating-plan thoughts/shared/plans/add-oauth.md
40
44
  ```
41
45
 
46
+ <details>
47
+ <summary>Manual setup (without the onboard skill)</summary>
48
+
49
+ If you prefer to set up manually instead of using `/onboard`:
50
+
51
+ ```bash
52
+ cd your-project
53
+
54
+ # Initialize thoughts
55
+ thc init
56
+
57
+ # Install skills to your AI agent
58
+ thc skill install
59
+ ```
60
+
61
+ </details>
62
+
42
63
  ## Skills
43
64
 
44
65
  Skills are installed by `thc skill install` and invoked as slash commands in your agent session:
45
66
 
46
67
  | Skill | Description |
47
68
  | -------------------- | --------------------------------------------------------------------- |
69
+ | `/onboard` | Initialize thoughts and bootstrap agent memory for a new project |
48
70
  | `/research-codebase` | Deep-dive into codebase, save findings to `thoughts/shared/research/` |
49
71
  | `/creating-plan` | Create implementation plan with phases and success criteria |
50
72
  | `/iterating-plan` | Refine existing plans based on feedback |
package/dist/index.js CHANGED
@@ -1095,81 +1095,92 @@ async function thoughtsInitCommand(options) {
1095
1095
  }
1096
1096
  let config = loadThoughtsConfig(options);
1097
1097
  if (!config) {
1098
- p.intro(chalk5.blue("Initial Thoughts Setup"));
1099
- p.log.info("First, let's configure your global thoughts system.");
1100
- const defaultRepo = getDefaultThoughtsRepo();
1101
- p.log.message(
1102
- chalk5.gray("This is where all your thoughts across all projects will be stored.")
1103
- );
1104
- const thoughtsRepoInput = await p.text({
1105
- message: "Thoughts repository location:",
1106
- initialValue: defaultRepo,
1107
- placeholder: defaultRepo
1108
- });
1109
- if (p.isCancel(thoughtsRepoInput)) {
1110
- p.cancel("Operation cancelled.");
1111
- process.exit(0);
1112
- }
1113
- const thoughtsRepo = thoughtsRepoInput || defaultRepo;
1114
- p.log.message(chalk5.gray("Your thoughts will be organized into two main directories:"));
1115
- p.log.message(chalk5.gray("- Repository-specific thoughts (one subdirectory per project)"));
1116
- p.log.message(chalk5.gray("- Global thoughts (shared across all projects)"));
1117
- const reposDirInput = await p.text({
1118
- message: "Directory name for repository-specific thoughts:",
1119
- initialValue: "repos",
1120
- placeholder: "repos"
1121
- });
1122
- if (p.isCancel(reposDirInput)) {
1123
- p.cancel("Operation cancelled.");
1124
- process.exit(0);
1125
- }
1126
- const reposDir2 = reposDirInput || "repos";
1127
- const globalDirInput = await p.text({
1128
- message: "Directory name for global thoughts:",
1129
- initialValue: "global",
1130
- placeholder: "global"
1131
- });
1132
- if (p.isCancel(globalDirInput)) {
1133
- p.cancel("Operation cancelled.");
1134
- process.exit(0);
1135
- }
1136
- const globalDir = globalDirInput || "global";
1137
- const defaultUser = process.env.USER || "user";
1138
- let user = "";
1139
- while (!user || user.toLowerCase() === "global") {
1140
- const userInput = await p.text({
1141
- message: "Your username:",
1142
- initialValue: defaultUser,
1143
- placeholder: defaultUser,
1144
- validate: (value) => {
1145
- if (value.toLowerCase() === "global") {
1146
- return `Username cannot be "global" as it's reserved for cross-project thoughts.`;
1147
- }
1148
- return void 0;
1149
- }
1098
+ if (options.directory) {
1099
+ const thoughtsRepo = getDefaultThoughtsRepo();
1100
+ const reposDir2 = "repos";
1101
+ const globalDir = "global";
1102
+ const user = process.env.USER || "user";
1103
+ config = { thoughtsRepo, reposDir: reposDir2, globalDir, user, repoMappings: {} };
1104
+ ensureThoughtsRepoExists(thoughtsRepo, reposDir2, globalDir);
1105
+ saveThoughtsConfig(config, options);
1106
+ p.log.success("Global thoughts configuration created with defaults");
1107
+ } else {
1108
+ p.intro(chalk5.blue("Initial Thoughts Setup"));
1109
+ p.log.info("First, let's configure your global thoughts system.");
1110
+ const defaultRepo = getDefaultThoughtsRepo();
1111
+ p.log.message(
1112
+ chalk5.gray("This is where all your thoughts across all projects will be stored.")
1113
+ );
1114
+ const thoughtsRepoInput = await p.text({
1115
+ message: "Thoughts repository location:",
1116
+ initialValue: defaultRepo,
1117
+ placeholder: defaultRepo
1150
1118
  });
1151
- if (p.isCancel(userInput)) {
1119
+ if (p.isCancel(thoughtsRepoInput)) {
1152
1120
  p.cancel("Operation cancelled.");
1153
1121
  process.exit(0);
1154
1122
  }
1155
- user = userInput || defaultUser;
1156
- }
1157
- config = {
1158
- thoughtsRepo,
1159
- reposDir: reposDir2,
1160
- globalDir,
1161
- user,
1162
- repoMappings: {}
1163
- };
1164
- p.note(
1165
- `${chalk5.cyan(thoughtsRepo)}/
1123
+ const thoughtsRepo = thoughtsRepoInput || defaultRepo;
1124
+ p.log.message(chalk5.gray("Your thoughts will be organized into two main directories:"));
1125
+ p.log.message(chalk5.gray("- Repository-specific thoughts (one subdirectory per project)"));
1126
+ p.log.message(chalk5.gray("- Global thoughts (shared across all projects)"));
1127
+ const reposDirInput = await p.text({
1128
+ message: "Directory name for repository-specific thoughts:",
1129
+ initialValue: "repos",
1130
+ placeholder: "repos"
1131
+ });
1132
+ if (p.isCancel(reposDirInput)) {
1133
+ p.cancel("Operation cancelled.");
1134
+ process.exit(0);
1135
+ }
1136
+ const reposDir2 = reposDirInput || "repos";
1137
+ const globalDirInput = await p.text({
1138
+ message: "Directory name for global thoughts:",
1139
+ initialValue: "global",
1140
+ placeholder: "global"
1141
+ });
1142
+ if (p.isCancel(globalDirInput)) {
1143
+ p.cancel("Operation cancelled.");
1144
+ process.exit(0);
1145
+ }
1146
+ const globalDir = globalDirInput || "global";
1147
+ const defaultUser = process.env.USER || "user";
1148
+ let user = "";
1149
+ while (!user || user.toLowerCase() === "global") {
1150
+ const userInput = await p.text({
1151
+ message: "Your username:",
1152
+ initialValue: defaultUser,
1153
+ placeholder: defaultUser,
1154
+ validate: (value) => {
1155
+ if (value.toLowerCase() === "global") {
1156
+ return `Username cannot be "global" as it's reserved for cross-project thoughts.`;
1157
+ }
1158
+ return void 0;
1159
+ }
1160
+ });
1161
+ if (p.isCancel(userInput)) {
1162
+ p.cancel("Operation cancelled.");
1163
+ process.exit(0);
1164
+ }
1165
+ user = userInput || defaultUser;
1166
+ }
1167
+ config = {
1168
+ thoughtsRepo,
1169
+ reposDir: reposDir2,
1170
+ globalDir,
1171
+ user,
1172
+ repoMappings: {}
1173
+ };
1174
+ p.note(
1175
+ `${chalk5.cyan(thoughtsRepo)}/
1166
1176
  \u251C\u2500\u2500 ${chalk5.cyan(reposDir2)}/ ${chalk5.gray("(project-specific thoughts)")}
1167
1177
  \u2514\u2500\u2500 ${chalk5.cyan(globalDir)}/ ${chalk5.gray("(cross-project thoughts)")}`,
1168
- "Creating thoughts structure"
1169
- );
1170
- ensureThoughtsRepoExists(thoughtsRepo, reposDir2, globalDir);
1171
- saveThoughtsConfig(config, options);
1172
- p.log.success("Global thoughts configuration created");
1178
+ "Creating thoughts structure"
1179
+ );
1180
+ ensureThoughtsRepoExists(thoughtsRepo, reposDir2, globalDir);
1181
+ saveThoughtsConfig(config, options);
1182
+ p.log.success("Global thoughts configuration created");
1183
+ }
1173
1184
  }
1174
1185
  if (options.profile) {
1175
1186
  if (!validateProfile(config, options.profile)) {
@@ -1261,18 +1272,16 @@ async function thoughtsInitCommand(options) {
1261
1272
  if (!mappedName) {
1262
1273
  if (options.directory) {
1263
1274
  const sanitizedDir = sanitizeDirectoryName(options.directory);
1264
- if (!existingRepos.includes(sanitizedDir)) {
1265
- p.log.error(`Directory "${sanitizedDir}" not found in thoughts repository.`);
1266
- p.log.error("In non-interactive mode (--directory), you must specify a directory");
1267
- p.log.error("name that already exists in the thoughts repository.");
1268
- p.log.warn("Available directories:");
1269
- existingRepos.forEach((repo) => p.log.message(chalk5.gray(` - ${repo}`)));
1270
- process.exit(1);
1275
+ if (existingRepos.includes(sanitizedDir)) {
1276
+ p.log.success(
1277
+ `Using existing: ${tempProfileConfig.thoughtsRepo}/${tempProfileConfig.reposDir}/${sanitizedDir}`
1278
+ );
1279
+ } else {
1280
+ p.log.success(
1281
+ `Will create: ${tempProfileConfig.thoughtsRepo}/${tempProfileConfig.reposDir}/${sanitizedDir}`
1282
+ );
1271
1283
  }
1272
1284
  mappedName = sanitizedDir;
1273
- p.log.success(
1274
- `Using existing: ${tempProfileConfig.thoughtsRepo}/${tempProfileConfig.reposDir}/${mappedName}`
1275
- );
1276
1285
  } else {
1277
1286
  p.intro(chalk5.blue("Repository Setup"));
1278
1287
  p.log.info(`Setting up thoughts for: ${chalk5.cyan(currentRepo)}`);