@pedrocivita/tocket 1.1.0 → 1.2.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.
@@ -1,31 +1,171 @@
1
1
  import { input } from "@inquirer/prompts";
2
- import { mkdir, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, writeFile, access } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
- import { claudeMd, geminiMd, tocketMd, activeContextMd, systemPatternsMd, productContextMd, techContextMd, progressMd, } from "../templates/memory-bank.js";
4
+ import { claudeMd, geminiMd, tocketMd, activeContextMd, systemPatternsMd, productContextMd, techContextMd, progressMd, cursorrulesMd, } from "../templates/memory-bank.js";
5
+ async function fileExists(path) {
6
+ try {
7
+ await access(path);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ function detectFramework(deps, devDeps) {
15
+ const all = [...deps, ...devDeps];
16
+ if (all.includes("next"))
17
+ return "Next.js";
18
+ if (all.includes("nuxt"))
19
+ return "Nuxt";
20
+ if (all.includes("@sveltejs/kit"))
21
+ return "SvelteKit";
22
+ if (all.includes("remix") || all.includes("@remix-run/react"))
23
+ return "Remix";
24
+ if (all.includes("astro"))
25
+ return "Astro";
26
+ if (all.includes("react"))
27
+ return "React";
28
+ if (all.includes("vue"))
29
+ return "Vue";
30
+ if (all.includes("svelte"))
31
+ return "Svelte";
32
+ if (all.includes("angular") || all.includes("@angular/core"))
33
+ return "Angular";
34
+ if (all.includes("express"))
35
+ return "Express";
36
+ if (all.includes("fastify"))
37
+ return "Fastify";
38
+ if (all.includes("hono"))
39
+ return "Hono";
40
+ if (all.includes("commander") || all.includes("yargs"))
41
+ return "CLI (Node.js)";
42
+ return "";
43
+ }
44
+ function detectBuild(devDeps) {
45
+ if (devDeps.includes("vite"))
46
+ return "Vite";
47
+ if (devDeps.includes("webpack"))
48
+ return "Webpack";
49
+ if (devDeps.includes("esbuild"))
50
+ return "esbuild";
51
+ if (devDeps.includes("rollup"))
52
+ return "Rollup";
53
+ if (devDeps.includes("turbopack") || devDeps.includes("turbo"))
54
+ return "Turbopack";
55
+ if (devDeps.includes("typescript"))
56
+ return "tsc";
57
+ return "";
58
+ }
59
+ function pickExtras(deps, devDeps) {
60
+ const notable = [
61
+ "tailwindcss", "prisma", "@prisma/client",
62
+ "drizzle-orm", "mongoose", "sequelize",
63
+ "trpc", "@trpc/server", "graphql",
64
+ "zod", "joi", "yup",
65
+ "jest", "vitest", "mocha",
66
+ "eslint", "prettier", "biome",
67
+ "docker-compose", "firebase", "supabase",
68
+ "stripe", "clerk", "@clerk/nextjs",
69
+ "socket.io", "redis", "bullmq",
70
+ ];
71
+ const all = [...deps, ...devDeps];
72
+ return notable.filter((n) => all.includes(n));
73
+ }
74
+ async function detectStack(cwd) {
75
+ const empty = {
76
+ language: "",
77
+ runtime: "",
78
+ build: "",
79
+ framework: "",
80
+ extras: [],
81
+ };
82
+ const pkgPath = join(cwd, "package.json");
83
+ if (!(await fileExists(pkgPath))) {
84
+ return { stack: empty, detectedName: "", detectedDescription: "" };
85
+ }
86
+ let pkg;
87
+ try {
88
+ const raw = await readFile(pkgPath, "utf-8");
89
+ pkg = JSON.parse(raw);
90
+ }
91
+ catch {
92
+ return { stack: empty, detectedName: "", detectedDescription: "" };
93
+ }
94
+ const deps = Object.keys(pkg.dependencies ?? {});
95
+ const devDeps = Object.keys(pkg.devDependencies ?? {});
96
+ const hasTsConfig = await fileExists(join(cwd, "tsconfig.json"));
97
+ const hasTs = hasTsConfig || devDeps.includes("typescript");
98
+ const stack = {
99
+ language: hasTs ? "TypeScript" : "JavaScript",
100
+ runtime: "Node.js",
101
+ build: detectBuild(devDeps),
102
+ framework: detectFramework(deps, devDeps),
103
+ extras: pickExtras(deps, devDeps),
104
+ };
105
+ const rawName = pkg.name ?? "";
106
+ const detectedName = rawName.startsWith("@")
107
+ ? rawName.split("/").pop() ?? rawName
108
+ : rawName;
109
+ return {
110
+ stack,
111
+ detectedName,
112
+ detectedDescription: pkg.description ?? "",
113
+ };
114
+ }
5
115
  export function registerInitCommand(program) {
6
116
  program
7
117
  .command("init")
8
118
  .description("Scaffold an agentic workspace with Memory Bank and triangulation config")
9
119
  .action(async () => {
10
- const projectName = await input({ message: "Project Name:" });
11
- const description = await input({ message: "Short Description:" });
12
- const contextDir = join(process.cwd(), ".context");
120
+ const cwd = process.cwd();
121
+ const { stack, detectedName, detectedDescription } = await detectStack(cwd);
122
+ const hasDetection = Boolean(stack.language);
123
+ if (hasDetection) {
124
+ console.log("\n Auto-detected stack from package.json:");
125
+ if (stack.language)
126
+ console.log(` Language: ${stack.language}`);
127
+ if (stack.runtime)
128
+ console.log(` Runtime: ${stack.runtime}`);
129
+ if (stack.build)
130
+ console.log(` Build: ${stack.build}`);
131
+ if (stack.framework)
132
+ console.log(` Framework: ${stack.framework}`);
133
+ if (stack.extras.length)
134
+ console.log(` Extras: ${stack.extras.join(", ")}`);
135
+ console.log();
136
+ }
137
+ const projectName = await input({
138
+ message: "Project Name:",
139
+ default: detectedName || undefined,
140
+ });
141
+ const description = await input({
142
+ message: "Short Description:",
143
+ default: detectedDescription || undefined,
144
+ });
145
+ const contextDir = join(cwd, ".context");
13
146
  await mkdir(contextDir, { recursive: true });
14
147
  const files = [
15
148
  ["TOCKET.md", tocketMd(projectName)],
16
149
  ["CLAUDE.md", claudeMd(projectName, description)],
17
150
  ["GEMINI.md", geminiMd(projectName, description)],
151
+ [".cursorrules", cursorrulesMd(projectName, description)],
18
152
  [join(".context", "activeContext.md"), activeContextMd(projectName)],
19
153
  [join(".context", "systemPatterns.md"), systemPatternsMd(projectName)],
20
- [join(".context", "productContext.md"), productContextMd(projectName, description)],
21
- [join(".context", "techContext.md"), techContextMd(projectName)],
154
+ [
155
+ join(".context", "productContext.md"),
156
+ productContextMd(projectName, description),
157
+ ],
158
+ [
159
+ join(".context", "techContext.md"),
160
+ techContextMd(projectName, hasDetection ? stack : undefined),
161
+ ],
22
162
  [join(".context", "progress.md"), progressMd(projectName)],
23
163
  ];
24
164
  for (const [filePath, content] of files) {
25
- const fullPath = join(process.cwd(), filePath);
165
+ const fullPath = join(cwd, filePath);
26
166
  await writeFile(fullPath, content, "utf-8");
27
167
  console.log(` created ${filePath}`);
28
168
  }
29
- console.log(`\nAgentic workspace initialized for ${projectName}! Ready to launch.`);
169
+ console.log(`\nAgentic workspace initialized for ${projectName}!${hasDetection ? " Stack pre-populated from package.json." : ""} Ready to launch.`);
30
170
  });
31
171
  }
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ const program = new Command();
8
8
  program
9
9
  .name("tocket")
10
10
  .description("The Context Engineering Framework for Multi-Agent Workspaces")
11
- .version("1.1.0");
11
+ .version("1.2.0");
12
12
  registerInitCommand(program);
13
13
  registerGenerateCommand(program);
14
14
  registerSyncCommand(program);
@@ -1,8 +1,16 @@
1
+ export interface StackInfo {
2
+ language: string;
3
+ runtime: string;
4
+ build: string;
5
+ framework: string;
6
+ extras: string[];
7
+ }
1
8
  export declare const claudeMd: (projectName: string, description: string) => string;
2
9
  export declare const geminiMd: (projectName: string, description: string) => string;
3
10
  export declare const activeContextMd: (projectName: string) => string;
4
11
  export declare const systemPatternsMd: (projectName: string) => string;
5
12
  export declare const productContextMd: (projectName: string, description: string) => string;
6
- export declare const techContextMd: (projectName: string) => string;
13
+ export declare const techContextMd: (projectName: string, stack?: StackInfo) => string;
7
14
  export declare const progressMd: (projectName: string) => string;
15
+ export declare const cursorrulesMd: (projectName: string, description: string) => string;
8
16
  export declare const tocketMd: (projectName: string) => string;
@@ -163,7 +163,15 @@ _Who is this for?_
163
163
 
164
164
  - _List the guiding principles for this project_
165
165
  `;
166
- export const techContextMd = (projectName) => `# Tech Context - ${projectName}
166
+ export const techContextMd = (projectName, stack) => {
167
+ const lang = stack?.language || "";
168
+ const rt = stack?.runtime || "";
169
+ const bld = stack?.build || "";
170
+ const fw = stack?.framework || "";
171
+ const extras = stack?.extras?.length
172
+ ? `\n### Notable Dependencies\n\n${stack.extras.map((d) => `- \`${d}\``).join("\n")}\n`
173
+ : "";
174
+ return `# Tech Context - ${projectName}
167
175
 
168
176
  <!-- Stack, build tools, and critical rules. Updated by the Architect. -->
169
177
 
@@ -171,11 +179,11 @@ export const techContextMd = (projectName) => `# Tech Context - ${projectName}
171
179
 
172
180
  | Layer | Technology | Notes |
173
181
  |-------|-----------|-------|
174
- | Language | | |
175
- | Runtime | | |
176
- | Build | | |
177
- | Framework | | |
178
-
182
+ | Language | ${lang} | ${lang ? "Auto-detected by Tocket" : ""} |
183
+ | Runtime | ${rt} | ${rt ? "Auto-detected by Tocket" : ""} |
184
+ | Build | ${bld} | ${bld ? "Auto-detected by Tocket" : ""} |
185
+ | Framework | ${fw} | ${fw ? "Auto-detected by Tocket" : ""} |
186
+ ${extras}
179
187
  ## Critical Rules
180
188
 
181
189
  _Document any rules that agents must follow (e.g., import conventions, naming patterns)._
@@ -188,6 +196,7 @@ ${projectName}/
188
196
  .context/ # Memory Bank
189
197
  \`\`\`
190
198
  `;
199
+ };
191
200
  export const progressMd = (projectName) => `# Progress Log - ${projectName}
192
201
 
193
202
  <!-- Appended by tocket sync and manual updates -->
@@ -203,6 +212,38 @@ export const progressMd = (projectName) => `# Progress Log - ${projectName}
203
212
 
204
213
  - [ ] _Define your first milestone here_
205
214
  `;
215
+ export const cursorrulesMd = (projectName, description) => `# .cursorrules - ${projectName}
216
+
217
+ # Generated by Tocket CLI — compatible with Cursor IDE
218
+
219
+ ## Role
220
+
221
+ You are the **Executor** for **${projectName}**.
222
+ ${description ? `\n> ${description}\n` : ""}
223
+ Your job is to **implement**, not to plan. Read the Memory Bank before every session, follow the Architect's decisions, and write code.
224
+
225
+ ## Rules
226
+
227
+ 1. **Always read \`.context/\` first** — Start every session by reading activeContext.md and systemPatterns.md.
228
+ 2. **Follow the Architect's plan** — Implementation decisions come from mission briefs. Do not redesign.
229
+ 3. **Ask before deviating** — If the plan is unclear or blocked, ask the user. Do not improvise architecture.
230
+ 4. **Write code in English** — Variables, functions, comments, commits — all in \`en-US\`.
231
+ 5. **Update Memory Bank on completion** — After finishing a task, update activeContext.md with what changed.
232
+
233
+ ## Memory Bank
234
+
235
+ | File | Purpose |
236
+ |------|---------|
237
+ | \`.context/activeContext.md\` | Current focus, recent changes, open decisions |
238
+ | \`.context/systemPatterns.md\` | Architecture patterns, tech stack, conventions |
239
+ | \`.context/techContext.md\` | Stack, build tools, critical rules |
240
+
241
+ ## Workflow
242
+
243
+ \`\`\`
244
+ 1. Read .context/ → 2. Receive task → 3. Implement → 4. Update .context/
245
+ \`\`\`
246
+ `;
206
247
  export const tocketMd = (projectName) => `# Tocket Protocol Specification
207
248
 
208
249
  > How any AI agent should operate in **${projectName}**.
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { claudeMd, geminiMd, tocketMd, activeContextMd, systemPatternsMd, productContextMd, techContextMd, progressMd, } from "../templates/memory-bank.js";
3
+ import { claudeMd, geminiMd, tocketMd, activeContextMd, systemPatternsMd, productContextMd, techContextMd, progressMd, cursorrulesMd, } from "../templates/memory-bank.js";
4
4
  describe("claudeMd", () => {
5
5
  const output = claudeMd("TestProject", "A test project");
6
6
  it("includes the project name in the title", () => {
@@ -106,17 +106,63 @@ describe("productContextMd", () => {
106
106
  });
107
107
  });
108
108
  describe("techContextMd", () => {
109
- const output = techContextMd("TestProject");
110
109
  it("includes the project name", () => {
110
+ const output = techContextMd("TestProject");
111
111
  assert.ok(output.includes("# Tech Context - TestProject"));
112
112
  });
113
113
  it("has a stack table", () => {
114
+ const output = techContextMd("TestProject");
114
115
  assert.ok(output.includes("| Language |"));
115
116
  assert.ok(output.includes("| Runtime |"));
116
117
  });
117
118
  it("includes project structure with project name", () => {
119
+ const output = techContextMd("TestProject");
118
120
  assert.ok(output.includes("TestProject/"));
119
121
  });
122
+ it("renders empty rows when no stack provided", () => {
123
+ const output = techContextMd("TestProject");
124
+ // Should have empty cells (no "Auto-detected")
125
+ assert.ok(!output.includes("Auto-detected"));
126
+ });
127
+ it("renders detected stack when StackInfo is provided", () => {
128
+ const stack = {
129
+ language: "TypeScript",
130
+ runtime: "Node.js",
131
+ build: "Vite",
132
+ framework: "React",
133
+ extras: ["tailwindcss", "prisma"],
134
+ };
135
+ const output = techContextMd("TestProject", stack);
136
+ assert.ok(output.includes("TypeScript"));
137
+ assert.ok(output.includes("Node.js"));
138
+ assert.ok(output.includes("Vite"));
139
+ assert.ok(output.includes("React"));
140
+ assert.ok(output.includes("Auto-detected by Tocket"));
141
+ });
142
+ it("renders notable dependencies when extras are present", () => {
143
+ const stack = {
144
+ language: "TypeScript",
145
+ runtime: "Node.js",
146
+ build: "tsc",
147
+ framework: "",
148
+ extras: ["tailwindcss", "zod"],
149
+ };
150
+ const output = techContextMd("TestProject", stack);
151
+ assert.ok(output.includes("### Notable Dependencies"));
152
+ assert.ok(output.includes("`tailwindcss`"));
153
+ assert.ok(output.includes("`zod`"));
154
+ });
155
+ it("omits notable dependencies section when extras are empty", () => {
156
+ const stack = {
157
+ language: "TypeScript",
158
+ runtime: "Node.js",
159
+ build: "tsc",
160
+ framework: "",
161
+ extras: [],
162
+ };
163
+ const output = techContextMd("TestProject", stack);
164
+ assert.ok(!output.includes("### Notable Dependencies"));
165
+ });
120
166
  });
121
167
  describe("progressMd", () => {
122
168
  const output = progressMd("TestProject");
@@ -130,3 +176,24 @@ describe("progressMd", () => {
130
176
  assert.ok(output.includes("## Next Up"));
131
177
  });
132
178
  });
179
+ describe("cursorrulesMd", () => {
180
+ const output = cursorrulesMd("TestProject", "A cool CLI tool");
181
+ it("includes the project name", () => {
182
+ assert.ok(output.includes("# .cursorrules - TestProject"));
183
+ });
184
+ it("includes the description as a blockquote", () => {
185
+ assert.ok(output.includes("> A cool CLI tool"));
186
+ });
187
+ it("defines the Executor role", () => {
188
+ assert.ok(output.includes("**Executor**"));
189
+ });
190
+ it("references Memory Bank files", () => {
191
+ assert.ok(output.includes(".context/activeContext.md"));
192
+ assert.ok(output.includes(".context/systemPatterns.md"));
193
+ });
194
+ it("handles empty description gracefully", () => {
195
+ const noDesc = cursorrulesMd("Foo", "");
196
+ assert.ok(noDesc.includes("# .cursorrules - Foo"));
197
+ assert.ok(!noDesc.includes("> \n"));
198
+ });
199
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pedrocivita/tocket",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "The Context Engineering Framework for Multi-Agent Workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",