@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
|
|
11
|
-
const
|
|
12
|
-
const
|
|
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
|
-
[
|
|
21
|
-
|
|
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(
|
|
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}
|
|
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.
|
|
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) =>
|
|
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
|
+
});
|