@pedrocivita/tocket 1.0.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.
package/README.md CHANGED
@@ -1,38 +1,110 @@
1
+ ![CI](https://github.com/pedrocivita/tocket/actions/workflows/ci.yml/badge.svg)
2
+ [![npm](https://img.shields.io/npm/v/@pedrocivita/tocket)](https://www.npmjs.com/package/@pedrocivita/tocket)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+
1
5
  # Tocket
2
6
 
3
7
  **The Context Engineering Framework for Multi-Agent Workspaces**
4
8
 
5
- Tocket structures how AI agents share context, hand off work, and build on each other's output. It turns ad-hoc prompting into a repeatable engineering workflow.
9
+ When multiple AI agents work on the same codebase, context is lost between sessions and between agents. Each one starts from scratch, re-reads files, and makes decisions that conflict with previous ones.
6
10
 
7
- ## Core Pillars
11
+ Tocket fixes this with two primitives:
8
12
 
9
- - **Triangulation** — Architect + Executor pattern. One agent plans, another implements, with structured handoff payloads between them.
10
- - **Context as Code** — Memory Bank protocol. Project context lives in version-controlled files (`.context/`), not ephemeral chat history.
13
+ - **Memory Bank** — A `.context/` directory with version-controlled markdown files that any AI can read. The project's ground truth lives in files, not in chat history.
14
+ - **Triangulation** — An Architect plans, an Executor implements, and structured XML payloads are the handoff between them.
11
15
 
12
- ## Commands
16
+ ## Who is this for?
17
+
18
+ - Developers using **multi-agent setups** (Gemini + Claude, Cursor + Copilot, etc.)
19
+ - Teams that want **reproducible AI-assisted development** across sessions
20
+ - Anyone tired of re-explaining project context to AI every time they open a chat
21
+
22
+ ## You don't need the CLI to use Tocket
13
23
 
14
- | Command | Description |
15
- |---------|-------------|
16
- | `tocket init` | Scaffold an agentic workspace with Memory Bank and triangulation config |
17
- | `tocket generate` | Build payload XMLs interactively for Architect-Executor handoff |
18
- | `tocket sync` | Update Memory Bank from git history and session artifacts |
24
+ The protocol is just files. You can adopt it manually:
19
25
 
20
- ## Installation
26
+ 1. Create a `.context/` directory with `activeContext.md` and `systemPatterns.md`
27
+ 2. Add a `TOCKET.md` to your repo root (see [the spec](TOCKET.md))
28
+ 3. Tell your AI agents to read `.context/` before acting
29
+
30
+ The CLI just automates the scaffolding.
31
+
32
+ ## Quick Start
21
33
 
22
34
  ```bash
23
- npx @pedrocivita/tocket
35
+ # Scaffold a new workspace (creates .context/, TOCKET.md, CLAUDE.md, GEMINI.md)
36
+ npx @pedrocivita/tocket init
37
+
38
+ # Generate a payload XML for agent handoff
39
+ npx @pedrocivita/tocket generate
40
+
41
+ # Sync session progress into Memory Bank
42
+ npx @pedrocivita/tocket sync
24
43
  ```
25
44
 
26
- ## Development
45
+ ## Commands
27
46
 
28
- ```bash
29
- git clone https://github.com/pedrocivita/tocket.git
30
- cd tocket
31
- npm install
32
- npm run build
33
- node dist/index.js --help
47
+ | Command | What it does |
48
+ | ----------------- | -------------------------------------------------------------- |
49
+ | `tocket init` | Scaffolds `.context/`, `TOCKET.md`, `CLAUDE.md`, `GEMINI.md` |
50
+ | `tocket generate` | Interactive payload XML builder — copies to clipboard |
51
+ | `tocket sync` | Appends session summary + git log to `.context/progress.md` |
52
+ | `tocket validate` | Checks if the current directory has a valid Tocket Memory Bank |
53
+
54
+ ## How Triangulation works
55
+
56
+ ```
57
+ Architect (any planning AI) Executor (any coding AI)
58
+ │ │
59
+ │ 1. Reads .context/ │
60
+ │ 2. Analyzes task │
61
+ │ 3. Generates <payload> XML │
62
+ │──────── structured handoff ──────►│
63
+ │ │ 4. Reads .context/ + payload
64
+ │ │ 5. Implements tasks
65
+ │ │ 6. Updates .context/
66
+ │◄──────── status report ───────────│
34
67
  ```
35
68
 
69
+ The Architect doesn't write code. The Executor doesn't make architecture decisions. The payload is the contract between them.
70
+
71
+ ## Memory Bank files
72
+
73
+ ```
74
+ .context/
75
+ activeContext.md ← Current focus. Read this first.
76
+ systemPatterns.md ← Architecture decisions and conventions.
77
+ techContext.md ← Stack, build tools, critical rules.
78
+ productContext.md ← What the product is and why it exists.
79
+ progress.md ← What's done, what's next.
80
+ ```
81
+
82
+ All files are markdown. All files are committed to git. Any AI that can read files can participate.
83
+
84
+ ## Documentation
85
+
86
+ | Guide | Description |
87
+ | ------------------------------------------- | ----------------------------------------------- |
88
+ | [Getting Started](docs/GETTING_STARTED.md) | Set up your first Tocket workspace in 5 minutes |
89
+ | [Tocket Rules](docs/TOCKET_RULES.md) | Complete reference for all protocol rules |
90
+ | [Developer Guide](docs/DEVELOPERS_GUIDE.md) | Contributing to the Tocket CLI codebase |
91
+ | [Protocol Spec](TOCKET.md) | The agent-agnostic protocol specification |
92
+
93
+ ## How is this different from...
94
+
95
+ | Tool | What it does | How Tocket differs |
96
+ | ---------------- | ------------------------------------ | ------------------------------------------------------------------------------------ |
97
+ | `.cursorrules` | Single-agent instructions for Cursor | Tocket defines _inter-agent_ protocol, not just single-agent rules |
98
+ | `CLAUDE.md` | Instructions for Claude Code | Tocket generates `CLAUDE.md` _as part of_ a broader multi-agent system |
99
+ | `AGENTS.md` | Codex agent instructions | Same idea for one agent; Tocket coordinates multiple agents |
100
+ | Prompt templates | Static prompts for LLMs | Tocket's Memory Bank evolves with the project; payloads are structured, not freeform |
101
+
102
+ ## Contributing
103
+
104
+ We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
105
+
106
+ This project follows a [Code of Conduct](CODE_OF_CONDUCT.md).
107
+
36
108
  ## License
37
109
 
38
110
  MIT
@@ -1,43 +1,31 @@
1
- import { input, select, number } from "@inquirer/prompts";
1
+ import { input, select } from "@inquirer/prompts";
2
2
  import clipboard from "clipboardy";
3
- function inferMode(score) {
4
- if (score <= 6)
5
- return "solo";
6
- if (score <= 10)
7
- return "team-small";
8
- return "team-full";
9
- }
10
3
  function buildPayloadXml(opts) {
11
- const mode = inferMode(opts.complexity);
12
- const skillLines = opts.skills
13
- .split(",")
14
- .map((s) => s.trim())
15
- .filter(Boolean)
16
- .map((s) => ` <skill>${s}</skill>`)
17
- .join("\n");
18
- return `<payload version="3.0">
4
+ const skillsAttr = opts.skills.trim()
5
+ ? `\n <skills>${opts.skills.trim()}</skills>`
6
+ : "";
7
+ return `<payload version="2.0">
19
8
  <meta>
20
9
  <intent>${opts.intent}</intent>
21
- <scope>${opts.scope}</scope>
10
+ <scope>${opts.scope}</scope>${skillsAttr}
22
11
  <priority>${opts.priority}</priority>
23
- <complexity score="${opts.complexity}" mode="${mode}" />
24
- <model>${opts.model}</model>
25
12
  </meta>
26
13
 
27
- <skills>
28
- ${skillLines || " <!-- Add skills here -->"}
29
- </skills>
30
-
31
14
  <context>
32
- <!-- Provide relevant context for the Architect -->
15
+ <summary><!-- Background and reasoning --></summary>
33
16
  </context>
34
17
 
35
18
  <tasks>
36
- <!-- The Architect will populate tasks based on the intent -->
19
+ <task id="1" type="create | edit | delete">
20
+ <target><!-- file/path --></target>
21
+ <action><!-- What to do --></action>
22
+ <spec><!-- Detailed specification --></spec>
23
+ <done><!-- Definition of done --></done>
24
+ </task>
37
25
  </tasks>
38
26
 
39
27
  <validate>
40
- <!-- Define acceptance criteria -->
28
+ <check><!-- How to verify success --></check>
41
29
  </validate>
42
30
  </payload>`;
43
31
  }
@@ -57,33 +45,10 @@ export function registerGenerateCommand(program) {
57
45
  ],
58
46
  });
59
47
  const skills = await input({
60
- message: "Skills/plugins (comma-separated):",
61
- });
62
- const model = await select({
63
- message: "Architect model:",
64
- choices: [
65
- {
66
- value: "gemini-3.1-pro-preview-customtools",
67
- name: "gemini-3.1-pro-preview-customtools (default)",
68
- },
69
- { value: "gemini-2.5-pro", name: "gemini-2.5-pro" },
70
- ],
71
- });
72
- const complexity = await number({
73
- message: "Complexity score (0-15):",
74
- min: 0,
75
- max: 15,
76
- required: true,
77
- });
78
- const xml = buildPayloadXml({
79
- intent,
80
- scope,
81
- priority,
82
- skills,
83
- model,
84
- complexity: complexity ?? 0,
48
+ message: "Skills/plugins (comma-separated, optional):",
85
49
  });
50
+ const xml = buildPayloadXml({ intent, scope, priority, skills });
86
51
  clipboard.writeSync(xml);
87
- console.log("\n\x1b[32m✓\x1b[0m Payload XML (v3.0) copied to clipboard! Paste it into your Architect to continue.");
52
+ console.log("\n\x1b[32m\u2713\x1b[0m Payload XML (v2.0) copied to clipboard! Paste it into your Architect to continue.");
88
53
  });
89
54
  }
@@ -1,27 +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, activeContextMd, systemPatternsMd, } 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 = [
148
+ ["TOCKET.md", tocketMd(projectName)],
15
149
  ["CLAUDE.md", claudeMd(projectName, description)],
16
150
  ["GEMINI.md", geminiMd(projectName, description)],
151
+ [".cursorrules", cursorrulesMd(projectName, description)],
17
152
  [join(".context", "activeContext.md"), activeContextMd(projectName)],
18
153
  [join(".context", "systemPatterns.md"), systemPatternsMd(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
+ ],
162
+ [join(".context", "progress.md"), progressMd(projectName)],
19
163
  ];
20
164
  for (const [filePath, content] of files) {
21
- const fullPath = join(process.cwd(), filePath);
165
+ const fullPath = join(cwd, filePath);
22
166
  await writeFile(fullPath, content, "utf-8");
23
167
  console.log(` created ${filePath}`);
24
168
  }
25
- 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.`);
26
170
  });
27
171
  }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerValidateCommand(program: Command): void;
@@ -0,0 +1,90 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const PASS = "\x1b[32m\u2713\x1b[0m";
4
+ const WARN = "\x1b[33m\u26A0\x1b[0m";
5
+ const FAIL = "\x1b[31m\u2717\x1b[0m";
6
+ function checkFile(basePath, relativePath, required) {
7
+ const fullPath = join(basePath, relativePath);
8
+ if (existsSync(fullPath)) {
9
+ return { icon: PASS, message: `${relativePath} found` };
10
+ }
11
+ if (required) {
12
+ return { icon: FAIL, message: `${relativePath} missing (required)` };
13
+ }
14
+ return { icon: WARN, message: `${relativePath} missing (optional)` };
15
+ }
16
+ function checkStale(basePath, relativePath) {
17
+ const fullPath = join(basePath, relativePath);
18
+ if (!existsSync(fullPath))
19
+ return null;
20
+ const stats = statSync(fullPath);
21
+ const daysSinceModified = Math.floor((Date.now() - stats.mtimeMs) / (1000 * 60 * 60 * 24));
22
+ if (daysSinceModified > 7) {
23
+ return {
24
+ icon: WARN,
25
+ message: `${relativePath} last modified ${daysSinceModified} days ago (may be stale)`,
26
+ };
27
+ }
28
+ return null;
29
+ }
30
+ function checkAgentFile(basePath) {
31
+ const agentFiles = [
32
+ "CLAUDE.md",
33
+ "GEMINI.md",
34
+ "EXECUTOR.md",
35
+ "ARCHITECT.md",
36
+ ".cursorrules",
37
+ ];
38
+ const found = agentFiles.filter((f) => existsSync(join(basePath, f)));
39
+ if (found.length > 0) {
40
+ return {
41
+ icon: PASS,
42
+ message: `Agent config found: ${found.join(", ")}`,
43
+ };
44
+ }
45
+ return {
46
+ icon: WARN,
47
+ message: "No agent config file found (CLAUDE.md, GEMINI.md, etc.)",
48
+ };
49
+ }
50
+ export function registerValidateCommand(program) {
51
+ program
52
+ .command("validate")
53
+ .description("Check the health of a Tocket workspace")
54
+ .action(() => {
55
+ const cwd = process.cwd();
56
+ const results = [];
57
+ let hasFailure = false;
58
+ console.log("\nValidating Tocket workspace...\n");
59
+ // Required files
60
+ results.push(checkFile(cwd, join(".context"), true));
61
+ results.push(checkFile(cwd, join(".context", "activeContext.md"), true));
62
+ results.push(checkFile(cwd, join(".context", "systemPatterns.md"), true));
63
+ results.push(checkFile(cwd, "TOCKET.md", true));
64
+ // Optional files
65
+ results.push(checkFile(cwd, join(".context", "techContext.md"), false));
66
+ results.push(checkFile(cwd, join(".context", "productContext.md"), false));
67
+ results.push(checkFile(cwd, join(".context", "progress.md"), false));
68
+ // Agent config
69
+ results.push(checkAgentFile(cwd));
70
+ // Staleness check
71
+ const staleCheck = checkStale(cwd, join(".context", "activeContext.md"));
72
+ if (staleCheck) {
73
+ results.push(staleCheck);
74
+ }
75
+ // Print results
76
+ for (const r of results) {
77
+ console.log(` ${r.icon} ${r.message}`);
78
+ if (r.icon === FAIL)
79
+ hasFailure = true;
80
+ }
81
+ console.log("");
82
+ if (hasFailure) {
83
+ console.log("\x1b[31mWorkspace has issues.\x1b[0m Run \x1b[1mtocket init\x1b[0m to scaffold missing files.");
84
+ process.exitCode = 1;
85
+ }
86
+ else {
87
+ console.log("\x1b[32mWorkspace is healthy.\x1b[0m");
88
+ }
89
+ });
90
+ }
package/dist/index.js CHANGED
@@ -3,12 +3,14 @@ import { Command } from "commander";
3
3
  import { registerInitCommand } from "./commands/init.cmd.js";
4
4
  import { registerGenerateCommand } from "./commands/generate.cmd.js";
5
5
  import { registerSyncCommand } from "./commands/sync.cmd.js";
6
+ import { registerValidateCommand } from "./commands/validate.cmd.js";
6
7
  const program = new Command();
7
8
  program
8
9
  .name("tocket")
9
10
  .description("The Context Engineering Framework for Multi-Agent Workspaces")
10
- .version("1.0.0");
11
+ .version("1.2.0");
11
12
  registerInitCommand(program);
12
13
  registerGenerateCommand(program);
13
14
  registerSyncCommand(program);
15
+ registerValidateCommand(program);
14
16
  program.parse();
@@ -1,4 +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;
12
+ export declare const productContextMd: (projectName: string, description: string) => string;
13
+ export declare const techContextMd: (projectName: string, stack?: StackInfo) => string;
14
+ export declare const progressMd: (projectName: string) => string;
15
+ export declare const cursorrulesMd: (projectName: string, description: string) => string;
16
+ export declare const tocketMd: (projectName: string) => string;
@@ -53,7 +53,7 @@ Your job is to **analyze, plan, and decide**. You do not write code directly —
53
53
  ## Rules
54
54
 
55
55
  1. **Read \`.context/\` first** — Understand current state before planning.
56
- 2. **Produce mission briefs** — Use \`<mission-brief>\` XML format for handoff to the Executor.
56
+ 2. **Produce payloads** — Use \`<payload version="2.0">\` XML format for handoff to the Executor.
57
57
  3. **Never write code inline** — Provide specs, not implementations. The Executor handles code.
58
58
  4. **Update systemPatterns.md** — When you make architectural decisions, record them.
59
59
  5. **Think in constraints** — Define what the system should and should not do.
@@ -70,12 +70,12 @@ Your job is to **analyze, plan, and decide**. You do not write code directly —
70
70
 
71
71
  ---
72
72
 
73
- ## Mission Brief Format
73
+ ## Payload Format
74
74
 
75
75
  \`\`\`xml
76
- <mission-brief version="1.0">
76
+ <payload version="2.0">
77
77
  <meta>
78
- <goal>What needs to happen</goal>
78
+ <intent>What needs to happen</intent>
79
79
  <scope>Which files/modules are affected</scope>
80
80
  <priority>high | medium | low</priority>
81
81
  </meta>
@@ -94,7 +94,7 @@ Your job is to **analyze, plan, and decide**. You do not write code directly —
94
94
  <validate>
95
95
  <check>Verification step</check>
96
96
  </validate>
97
- </mission-brief>
97
+ </payload>
98
98
  \`\`\`
99
99
  `;
100
100
  export const activeContextMd = (projectName) => `# Active Context - ${projectName}
@@ -139,3 +139,209 @@ _To be defined._
139
139
  |----------|-----------|------|
140
140
  | Adopted Tocket triangulation | Structured multi-agent workflow | ${new Date().toISOString().split("T")[0]} |
141
141
  `;
142
+ export const productContextMd = (projectName, description) => `# Product Context - ${projectName}
143
+
144
+ <!-- What the product is, who it's for, and why it exists. Updated by the Architect. -->
145
+
146
+ ## What is ${projectName}?
147
+
148
+ ${description || "_Describe your product here._"}
149
+
150
+ ## Problem
151
+
152
+ _What problem does this project solve?_
153
+
154
+ ## Solution
155
+
156
+ _How does it solve it?_
157
+
158
+ ## Target Users
159
+
160
+ _Who is this for?_
161
+
162
+ ## Design Principles
163
+
164
+ - _List the guiding principles for this project_
165
+ `;
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}
175
+
176
+ <!-- Stack, build tools, and critical rules. Updated by the Architect. -->
177
+
178
+ ## Stack
179
+
180
+ | Layer | Technology | Notes |
181
+ |-------|-----------|-------|
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}
187
+ ## Critical Rules
188
+
189
+ _Document any rules that agents must follow (e.g., import conventions, naming patterns)._
190
+
191
+ ## Project Structure
192
+
193
+ \`\`\`
194
+ ${projectName}/
195
+ src/ # Source code
196
+ .context/ # Memory Bank
197
+ \`\`\`
198
+ `;
199
+ };
200
+ export const progressMd = (projectName) => `# Progress Log - ${projectName}
201
+
202
+ <!-- Appended by tocket sync and manual updates -->
203
+
204
+ ## Milestone: Project Initialization
205
+
206
+ **Status**: Complete
207
+
208
+ - [x] Agentic workspace scaffolded with Tocket CLI
209
+ - [x] Memory Bank initialized (\`.context/\`)
210
+
211
+ ## Next Up
212
+
213
+ - [ ] _Define your first milestone here_
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
+ `;
247
+ export const tocketMd = (projectName) => `# Tocket Protocol Specification
248
+
249
+ > How any AI agent should operate in **${projectName}**.
250
+
251
+ This file is **agent-agnostic**. Whether you are Claude, Gemini, GPT, Cursor, Cline, Copilot, or any future AI — if you see this file, follow these rules.
252
+
253
+ ---
254
+
255
+ ## 1. Memory Bank
256
+
257
+ Project context lives in \`.context/\`, not in chat history. **Read it before doing anything.**
258
+
259
+ \`\`\`
260
+ .context/
261
+ activeContext.md <- Start here. Current focus, recent changes, open decisions.
262
+ systemPatterns.md <- Architecture patterns, conventions, key decisions.
263
+ techContext.md <- Tech stack, build tools, critical rules.
264
+ productContext.md <- What the product is, who it's for, why it exists.
265
+ progress.md <- What's done, what's next.
266
+ \`\`\`
267
+
268
+ ### Rules
269
+
270
+ - **Read before acting** — Always read \`activeContext.md\` and \`systemPatterns.md\` before your first action in a session.
271
+ - **Write before leaving** — Update \`activeContext.md\` with what changed after completing significant work.
272
+ - **Trust the files** — If \`.context/\` says the project uses ESM, it uses ESM. Don't second-guess documented decisions.
273
+ - **Don't duplicate** — Context belongs in \`.context/\`, not scattered in code comments or chat summaries.
274
+
275
+ ---
276
+
277
+ ## 2. Triangulation
278
+
279
+ Tocket separates **planning** from **implementation** across two agent roles:
280
+
281
+ \`\`\`
282
+ ┌─────────────────┐ ┌─────────────────┐
283
+ │ ARCHITECT │ │ EXECUTOR │
284
+ │ (Planner) │ payload │ (Implementer) │
285
+ │ │─────────>│ │
286
+ │ Analyzes task │ │ Receives plan │
287
+ │ Designs approach│ │ Writes code │
288
+ │ Generates XML │ │ Runs tests │
289
+ │ Updates patterns│ │ Updates context │
290
+ └─────────────────┘ └─────────────────┘
291
+ \`\`\`
292
+
293
+ ### Architect
294
+
295
+ - Reads \`.context/\` to understand current state
296
+ - Produces structured payloads (see Section 3) with clear tasks
297
+ - Makes architectural decisions and records them in \`systemPatterns.md\`
298
+ - **Does not write code** — only specs and constraints
299
+
300
+ ### Executor
301
+
302
+ - Reads \`.context/\` and the Architect's payload
303
+ - Implements tasks exactly as specified
304
+ - Asks when the plan is unclear — does not improvise architecture
305
+ - Updates \`activeContext.md\` and \`progress.md\` after completing work
306
+
307
+ ### Solo Mode
308
+
309
+ Not every task needs triangulation. For simple, well-defined changes, a single agent can act as both Architect and Executor. The Memory Bank rules still apply.
310
+
311
+ ---
312
+
313
+ ## 3. Payloads
314
+
315
+ A payload is the structured handoff from Architect to Executor.
316
+
317
+ ### Minimal Example
318
+
319
+ \`\`\`xml
320
+ <payload version="2.0">
321
+ <meta>
322
+ <intent>Goal in one line</intent>
323
+ <scope>Files affected</scope>
324
+ <priority>high | medium | low</priority>
325
+ </meta>
326
+ <tasks>
327
+ <task id="1" type="create | edit | delete">
328
+ <target>file/path</target>
329
+ <action>What to do</action>
330
+ <done>Definition of done</done>
331
+ </task>
332
+ </tasks>
333
+ <validate>
334
+ <check>How to verify success</check>
335
+ </validate>
336
+ </payload>
337
+ \`\`\`
338
+
339
+ ---
340
+
341
+ ## Quick Start
342
+
343
+ 1. Read this file (\`TOCKET.md\`)
344
+ 2. Read \`.context/activeContext.md\` for current state
345
+ 3. Read your role-specific config (\`CLAUDE.md\` or \`GEMINI.md\`)
346
+ 4. Proceed with your task, following the Memory Bank rules above
347
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,199 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { claudeMd, geminiMd, tocketMd, activeContextMd, systemPatternsMd, productContextMd, techContextMd, progressMd, cursorrulesMd, } from "../templates/memory-bank.js";
4
+ describe("claudeMd", () => {
5
+ const output = claudeMd("TestProject", "A test project");
6
+ it("includes the project name in the title", () => {
7
+ assert.ok(output.includes("# CLAUDE.md - TestProject"));
8
+ });
9
+ it("includes the description as a blockquote", () => {
10
+ assert.ok(output.includes("> A test project"));
11
+ });
12
+ it("defines the Executor role", () => {
13
+ assert.ok(output.includes("**Executor**"));
14
+ });
15
+ it("references the Memory Bank files", () => {
16
+ assert.ok(output.includes(".context/activeContext.md"));
17
+ assert.ok(output.includes(".context/systemPatterns.md"));
18
+ });
19
+ it("handles empty description gracefully", () => {
20
+ const noDesc = claudeMd("Foo", "");
21
+ assert.ok(noDesc.includes("# CLAUDE.md - Foo"));
22
+ assert.ok(!noDesc.includes("> \n"));
23
+ });
24
+ });
25
+ describe("geminiMd", () => {
26
+ const output = geminiMd("TestProject", "A test project");
27
+ it("includes the project name in the title", () => {
28
+ assert.ok(output.includes("# GEMINI.md - TestProject"));
29
+ });
30
+ it("defines the Architect role", () => {
31
+ assert.ok(output.includes("**Architect**"));
32
+ });
33
+ it("references payload v2.0 format", () => {
34
+ assert.ok(output.includes('<payload version="2.0">'));
35
+ });
36
+ it("does not reference the deprecated mission-brief format", () => {
37
+ assert.ok(!output.includes("<mission-brief"));
38
+ });
39
+ });
40
+ describe("tocketMd", () => {
41
+ const output = tocketMd("TestProject");
42
+ it("includes the protocol title", () => {
43
+ assert.ok(output.includes("# Tocket Protocol Specification"));
44
+ });
45
+ it("includes the project name", () => {
46
+ assert.ok(output.includes("**TestProject**"));
47
+ });
48
+ it("declares agent-agnostic stance", () => {
49
+ assert.ok(output.includes("agent-agnostic"));
50
+ });
51
+ it("documents all 5 Memory Bank files", () => {
52
+ assert.ok(output.includes("activeContext.md"));
53
+ assert.ok(output.includes("systemPatterns.md"));
54
+ assert.ok(output.includes("techContext.md"));
55
+ assert.ok(output.includes("productContext.md"));
56
+ assert.ok(output.includes("progress.md"));
57
+ });
58
+ it("documents the Triangulation pattern", () => {
59
+ assert.ok(output.includes("ARCHITECT"));
60
+ assert.ok(output.includes("EXECUTOR"));
61
+ });
62
+ it("includes a payload example with v2.0", () => {
63
+ assert.ok(output.includes('<payload version="2.0">'));
64
+ });
65
+ });
66
+ describe("activeContextMd", () => {
67
+ const output = activeContextMd("TestProject");
68
+ it("includes the project name", () => {
69
+ assert.ok(output.includes("# Active Context - TestProject"));
70
+ });
71
+ it("has the required sections", () => {
72
+ assert.ok(output.includes("## Current Focus"));
73
+ assert.ok(output.includes("## Recent Changes"));
74
+ assert.ok(output.includes("## Open Decisions"));
75
+ });
76
+ it("includes a date in the recent changes table", () => {
77
+ assert.match(output, /\d{4}-\d{2}-\d{2}/);
78
+ });
79
+ });
80
+ describe("systemPatternsMd", () => {
81
+ const output = systemPatternsMd("TestProject");
82
+ it("includes the project name", () => {
83
+ assert.ok(output.includes("# System Patterns - TestProject"));
84
+ });
85
+ it("has the required sections", () => {
86
+ assert.ok(output.includes("## Tech Stack"));
87
+ assert.ok(output.includes("## Conventions"));
88
+ assert.ok(output.includes("## Key Decisions"));
89
+ });
90
+ });
91
+ describe("productContextMd", () => {
92
+ it("includes the project name and description", () => {
93
+ const output = productContextMd("TestProject", "A cool tool");
94
+ assert.ok(output.includes("# Product Context - TestProject"));
95
+ assert.ok(output.includes("A cool tool"));
96
+ });
97
+ it("shows placeholder when description is empty", () => {
98
+ const output = productContextMd("TestProject", "");
99
+ assert.ok(output.includes("_Describe your product here._"));
100
+ });
101
+ it("has the required sections", () => {
102
+ const output = productContextMd("TestProject", "desc");
103
+ assert.ok(output.includes("## Problem"));
104
+ assert.ok(output.includes("## Solution"));
105
+ assert.ok(output.includes("## Target Users"));
106
+ });
107
+ });
108
+ describe("techContextMd", () => {
109
+ it("includes the project name", () => {
110
+ const output = techContextMd("TestProject");
111
+ assert.ok(output.includes("# Tech Context - TestProject"));
112
+ });
113
+ it("has a stack table", () => {
114
+ const output = techContextMd("TestProject");
115
+ assert.ok(output.includes("| Language |"));
116
+ assert.ok(output.includes("| Runtime |"));
117
+ });
118
+ it("includes project structure with project name", () => {
119
+ const output = techContextMd("TestProject");
120
+ assert.ok(output.includes("TestProject/"));
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
+ });
166
+ });
167
+ describe("progressMd", () => {
168
+ const output = progressMd("TestProject");
169
+ it("includes the project name", () => {
170
+ assert.ok(output.includes("# Progress Log - TestProject"));
171
+ });
172
+ it("has an initialization milestone marked complete", () => {
173
+ assert.ok(output.includes("**Status**: Complete"));
174
+ });
175
+ it("has a Next Up section", () => {
176
+ assert.ok(output.includes("## Next Up"));
177
+ });
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.0.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",
@@ -14,7 +14,7 @@
14
14
  "build": "tsc",
15
15
  "start": "node dist/index.js",
16
16
  "prepublishOnly": "npm run build",
17
- "test": "echo \"Error: no test specified\" && exit 1"
17
+ "test": "npm run build && node --test dist/tests/*.test.js"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",