@pedrocivita/tocket 2.0.0 → 2.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.
Files changed (39) hide show
  1. package/README.md +193 -132
  2. package/dist/commands/dashboard.js +5 -0
  3. package/dist/commands/doctor.cmd.d.ts +9 -0
  4. package/dist/commands/doctor.cmd.js +191 -0
  5. package/dist/commands/eject.cmd.d.ts +6 -0
  6. package/dist/commands/eject.cmd.js +65 -0
  7. package/dist/commands/focus.cmd.d.ts +7 -0
  8. package/dist/commands/focus.cmd.js +52 -0
  9. package/dist/commands/generate.cmd.d.ts +8 -0
  10. package/dist/commands/generate.cmd.js +27 -10
  11. package/dist/commands/init.cmd.js +15 -3
  12. package/dist/commands/lint.cmd.d.ts +12 -0
  13. package/dist/commands/lint.cmd.js +269 -0
  14. package/dist/commands/status.cmd.d.ts +2 -0
  15. package/dist/commands/status.cmd.js +85 -0
  16. package/dist/commands/sync.cmd.js +3 -2
  17. package/dist/commands/validate.cmd.d.ts +7 -0
  18. package/dist/commands/validate.cmd.js +3 -3
  19. package/dist/index.js +10 -0
  20. package/dist/tests/doctor.test.d.ts +1 -0
  21. package/dist/tests/doctor.test.js +102 -0
  22. package/dist/tests/eject.test.d.ts +1 -0
  23. package/dist/tests/eject.test.js +88 -0
  24. package/dist/tests/focus.test.d.ts +1 -0
  25. package/dist/tests/focus.test.js +130 -0
  26. package/dist/tests/generate.test.d.ts +1 -0
  27. package/dist/tests/generate.test.js +67 -0
  28. package/dist/tests/git.test.js +9 -1
  29. package/dist/tests/init.test.d.ts +1 -0
  30. package/dist/tests/init.test.js +69 -0
  31. package/dist/tests/lint.test.d.ts +1 -0
  32. package/dist/tests/lint.test.js +120 -0
  33. package/dist/tests/status.test.d.ts +1 -0
  34. package/dist/tests/status.test.js +34 -0
  35. package/dist/tests/validate.test.d.ts +1 -0
  36. package/dist/tests/validate.test.js +92 -0
  37. package/dist/utils/git.d.ts +1 -0
  38. package/dist/utils/git.js +13 -0
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -1,132 +1,193 @@
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
-
5
- # Tocket
6
-
7
- **The Context Engineering Framework for Multi-Agent Workspaces**
8
-
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.
10
-
11
- Tocket fixes this with two primitives:
12
-
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.
15
-
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
23
-
24
- The protocol is just files. You can adopt it manually:
25
-
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
33
-
34
- ```bash
35
- # Interactive dashboard — guided entry point
36
- npx @pedrocivita/tocket
37
-
38
- # Scaffold a new workspace (creates .context/, TOCKET.md, CLAUDE.md, GEMINI.md)
39
- npx @pedrocivita/tocket init
40
-
41
- # Generate a payload XML with smart git integration
42
- npx @pedrocivita/tocket generate
43
-
44
- # Sync session progress into Memory Bank
45
- npx @pedrocivita/tocket sync
46
- ```
47
-
48
- ## Commands
49
-
50
- | Command | What it does |
51
- | ----------------- | -------------------------------------------------------------------- |
52
- | `tocket` | Interactive dashboard with guided menu |
53
- | `tocket init` | Scaffolds `.context/`, `TOCKET.md`, `CLAUDE.md`, `GEMINI.md` |
54
- | `tocket generate` | Smart payload builder — auto-fills scope from git, multi-task support |
55
- | `tocket sync` | Appends session summary + git log to `.context/progress.md` |
56
- | `tocket validate` | Checks if the current directory has a valid Tocket Memory Bank |
57
- | `tocket config` | Manage global settings (`~/.tocketrc.json`) |
58
-
59
- ## Configuration
60
-
61
- Set global defaults so you don't repeat yourself:
62
-
63
- ```bash
64
- # Interactive setup
65
- tocket config
66
-
67
- # Or use flags (CI-friendly)
68
- tocket config --author "Your Name" --priority medium --skills "core,lsp"
69
-
70
- # View current config
71
- tocket config --show
72
- ```
73
-
74
- Config is stored at `~/.tocketrc.json` and pre-fills author, priority, and skills in all commands.
75
-
76
- ## How Triangulation works
77
-
78
- ```
79
- Architect (any planning AI) Executor (any coding AI)
80
- │ │
81
- │ 1. Reads .context/ │
82
- │ 2. Analyzes task │
83
- │ 3. Generates <payload> XML │
84
- │──────── structured handoff ──────►│
85
- │ │ 4. Reads .context/ + payload
86
- │ │ 5. Implements tasks
87
- │ │ 6. Updates .context/
88
- │◄──────── status report ───────────│
89
- ```
90
-
91
- The Architect doesn't write code. The Executor doesn't make architecture decisions. The payload is the contract between them.
92
-
93
- ## Memory Bank files
94
-
95
- ```
96
- .context/
97
- activeContext.md ← Current focus. Read this first.
98
- systemPatterns.md ← Architecture decisions and conventions.
99
- techContext.md ← Stack, build tools, critical rules.
100
- productContext.md ← What the product is and why it exists.
101
- progress.md ← What's done, what's next.
102
- ```
103
-
104
- All files are markdown. All files are committed to git. Any AI that can read files can participate.
105
-
106
- ## Documentation
107
-
108
- | Guide | Description |
109
- | ------------------------------------------- | ----------------------------------------------- |
110
- | [Getting Started](docs/GETTING_STARTED.md) | Set up your first Tocket workspace in 5 minutes |
111
- | [Tocket Rules](docs/TOCKET_RULES.md) | Complete reference for all protocol rules |
112
- | [Developer Guide](docs/DEVELOPERS_GUIDE.md) | Contributing to the Tocket CLI codebase |
113
- | [Protocol Spec](TOCKET.md) | The agent-agnostic protocol specification |
114
-
115
- ## How is this different from...
116
-
117
- | Tool | What it does | How Tocket differs |
118
- | ---------------- | ------------------------------------ | ------------------------------------------------------------------------------------ |
119
- | `.cursorrules` | Single-agent instructions for Cursor | Tocket defines _inter-agent_ protocol, not just single-agent rules |
120
- | `CLAUDE.md` | Instructions for Claude Code | Tocket generates `CLAUDE.md` _as part of_ a broader multi-agent system |
121
- | `AGENTS.md` | Codex agent instructions | Same idea for one agent; Tocket coordinates multiple agents |
122
- | Prompt templates | Static prompts for LLMs | Tocket's Memory Bank evolves with the project; payloads are structured, not freeform |
123
-
124
- ## Contributing
125
-
126
- We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
127
-
128
- This project follows a [Code of Conduct](CODE_OF_CONDUCT.md).
129
-
130
- ## License
131
-
132
- MIT
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
+
5
+ # Tocket
6
+
7
+ **The Context Engineering Framework for Multi-Agent Workspaces**
8
+
9
+ AI agents forget everything between sessions. When multiple agents work on the same codebase, they re-read files, duplicate work, and make conflicting decisions. Tocket fixes this with **shared context files that any agent can read** no vendor lock-in, no special integrations.
10
+
11
+ <p align="center">
12
+ <img src="docs/assets/tocket-dashboard.png" alt="Tocket CLI Dashboard" width="700" />
13
+ </p>
14
+
15
+ ## The idea in 30 seconds
16
+
17
+ Tocket is a file convention. It adds a `.context/` directory to your repo with markdown files that describe your project's current state, architecture, and progress. Any AI agent that can read files — Claude, Gemini, GPT, Cursor, Copilot — can pick up where the last session left off.
18
+
19
+ ```
20
+ your-project/
21
+ .context/
22
+ activeContext.md # What's happening right now
23
+ systemPatterns.md # How the codebase is organized
24
+ techContext.md # Stack and build tools
25
+ productContext.md # What the product is and why
26
+ progress.md # What's done, what's next
27
+ TOCKET.md # Protocol rules (for any AI)
28
+ CLAUDE.md # Executor agent config
29
+ GEMINI.md # Architect agent config
30
+ ```
31
+
32
+ All files are plain markdown, committed to git, and readable by any tool.
33
+
34
+ ## You don't need the CLI
35
+
36
+ The protocol is just files. You can adopt it manually:
37
+
38
+ 1. Create a `.context/` directory with `activeContext.md` and `systemPatterns.md`
39
+ 2. Add a [`TOCKET.md`](TOCKET.md) to your repo root
40
+ 3. Tell your agents to read `.context/` before acting
41
+
42
+ The CLI automates the scaffolding, provides smart defaults, and adds quality-of-life tooling around the protocol.
43
+
44
+ ## Quick Start
45
+
46
+ ```bash
47
+ # Scaffold a new workspace (creates .context/, TOCKET.md, CLAUDE.md, GEMINI.md)
48
+ npx @pedrocivita/tocket init
49
+
50
+ # Or just the essentials (3 files)
51
+ npx @pedrocivita/tocket init --minimal
52
+
53
+ # Or open the interactive dashboard
54
+ npx @pedrocivita/tocket
55
+ ```
56
+
57
+ That's it. Your repo now has a Memory Bank. Every AI session starts by reading `.context/activeContext.md`.
58
+
59
+ ### Safe testing — use a branch
60
+
61
+ Tocket writes files to your repo, but you can try it risk-free on a branch:
62
+
63
+ ```bash
64
+ git checkout -b test/tocket-setup
65
+ npx @pedrocivita/tocket init
66
+ git add .context/ TOCKET.md CLAUDE.md GEMINI.md .cursorrules
67
+ git commit -m "chore: scaffold Tocket workspace"
68
+
69
+ # Try it out — run some AI sessions, see if you like it
70
+ # Don't like it? Clean up:
71
+ npx @pedrocivita/tocket eject # removes all Tocket files
72
+ # Or just delete the branch:
73
+ git checkout main && git branch -D test/tocket-setup
74
+ ```
75
+
76
+ See the [Developer Guide](docs/DEVELOPERS_GUIDE.md) for detailed safe-testing workflows.
77
+
78
+ ## Commands
79
+
80
+ | Command | What it does |
81
+ | --- | --- |
82
+ | `tocket` | Interactive dashboard with guided menu |
83
+ | `tocket init` | Scaffold `.context/`, `TOCKET.md`, and agent configs (auto-detects your stack) |
84
+ | `tocket generate` | Build structured payload XML (auto-fills scope from git) |
85
+ | `tocket sync` | Append session summary + git log to `.context/progress.md` |
86
+ | `tocket validate` | Check if the workspace has a valid Memory Bank |
87
+ | `tocket focus` | Update the Current Focus in `activeContext.md` |
88
+ | `tocket status` | Quick overview: workspace health, branch, focus, agents |
89
+ | `tocket doctor` | Deep workspace diagnostics (content health, git tracking, staleness) |
90
+ | `tocket lint` | Audit `.context/` content quality and suggest improvements |
91
+ | `tocket config` | Manage global settings (`~/.tocketrc.json`) |
92
+ | `tocket eject` | Remove all Tocket files (with confirmation) |
93
+
94
+ ### CI-friendly flags
95
+
96
+ Every interactive command has flags for non-interactive use:
97
+
98
+ ```bash
99
+ # Minimal init (3 files instead of 9)
100
+ tocket init --minimal --name myproject --description "My app" --force
101
+
102
+ # Sync without prompt
103
+ tocket sync --summary "Fixed auth bug and added tests"
104
+
105
+ # Generate to stdout or file instead of clipboard
106
+ tocket generate --to stdout
107
+ tocket generate --to payload.xml
108
+ ```
109
+
110
+ ## How it works
111
+
112
+ ### Memory Bank
113
+
114
+ The `.context/` directory is the project's shared memory. Agents read it before acting and update it after completing work. Context lives in files, not in chat history.
115
+
116
+ | File | Purpose | Updated |
117
+ | --- | --- | --- |
118
+ | `activeContext.md` | Current focus, recent changes, open decisions | Every session |
119
+ | `systemPatterns.md` | Architecture patterns and conventions | When patterns change |
120
+ | `techContext.md` | Stack, build tools, critical rules | When stack changes |
121
+ | `productContext.md` | What the product is and why | Rarely |
122
+ | `progress.md` | Milestones and completed work | Per milestone |
123
+
124
+ ### Triangulation
125
+
126
+ For complex tasks, Tocket separates planning from implementation:
127
+
128
+ ```
129
+ Architect (any planning AI) Executor (any coding AI)
130
+ | |
131
+ | 1. Reads .context/ |
132
+ | 2. Analyzes task |
133
+ | 3. Generates <payload> XML |
134
+ |-------- structured handoff ------>|
135
+ | | 4. Reads .context/ + payload
136
+ | | 5. Implements tasks
137
+ | | 6. Updates .context/
138
+ |<-------- status report -----------|
139
+ ```
140
+
141
+ The Architect doesn't write code. The Executor doesn't make architecture decisions. The payload XML is the contract between them. For simple tasks, a single agent can fill both roles.
142
+
143
+ ## Who is this for?
144
+
145
+ - Developers using **multi-agent setups** (Gemini + Claude, Cursor + Copilot, etc.)
146
+ - Teams that want **reproducible AI-assisted development** across sessions
147
+ - Anyone tired of re-explaining project context to AI every time they open a chat
148
+ - Open-source maintainers who want contributors' AI agents to follow project conventions
149
+
150
+ ## How is Tocket different?
151
+
152
+ | Tool | What it does | How Tocket differs |
153
+ | --- | --- | --- |
154
+ | `.cursorrules` | Single-agent instructions for Cursor | Tocket defines _inter-agent_ protocol, not just single-agent rules |
155
+ | `CLAUDE.md` | Instructions for Claude Code | Tocket generates `CLAUDE.md` as part of a broader multi-agent system |
156
+ | `AGENTS.md` | Codex agent instructions | Same idea for one agent; Tocket coordinates multiple agents |
157
+ | Prompt templates | Static prompts for LLMs | Tocket's Memory Bank evolves with the project; payloads are structured, not freeform |
158
+
159
+ ## Configuration
160
+
161
+ Set global defaults so you don't repeat yourself:
162
+
163
+ ```bash
164
+ # Interactive setup
165
+ tocket config
166
+
167
+ # Or use flags (CI-friendly)
168
+ tocket config --author "Your Name" --priority medium --skills "core,lsp"
169
+
170
+ # View current config
171
+ tocket config --show
172
+ ```
173
+
174
+ Config is stored at `~/.tocketrc.json` and pre-fills author, priority, and skills in all commands.
175
+
176
+ ## Documentation
177
+
178
+ | Guide | Description |
179
+ | --- | --- |
180
+ | [Getting Started](docs/GETTING_STARTED.md) | Set up your first Tocket workspace in 5 minutes |
181
+ | [Developer Guide](docs/DEVELOPERS_GUIDE.md) | How to run the Tocket protocol safely in any project |
182
+ | [Tocket Rules](docs/TOCKET_RULES.md) | Complete reference for all protocol rules |
183
+ | [Protocol Spec](TOCKET.md) | The agent-agnostic protocol specification |
184
+
185
+ ## Contributing
186
+
187
+ We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
188
+
189
+ This project follows a [Code of Conduct](CODE_OF_CONDUCT.md).
190
+
191
+ ## License
192
+
193
+ MIT
@@ -44,7 +44,12 @@ export async function showDashboard(program) {
44
44
  { value: "generate", name: "Generate payload" },
45
45
  { value: "sync", name: "Sync progress" },
46
46
  { value: "validate", name: "Validate workspace" },
47
+ { value: "focus", name: "Update focus" },
48
+ { value: "status", name: "Workspace status" },
49
+ { value: "doctor", name: "Run diagnostics" },
50
+ { value: "lint", name: "Lint context quality" },
47
51
  { value: "config", name: "Configure settings" },
52
+ { value: "eject", name: "Eject workspace" },
48
53
  { value: "exit", name: "Exit" },
49
54
  ]
50
55
  : [
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ export interface DiagResult {
3
+ icon: string;
4
+ message: string;
5
+ }
6
+ export declare function checkContentHealth(basePath: string): DiagResult[];
7
+ export declare function checkGitTracking(cwd: string): DiagResult[];
8
+ export declare function checkStaleness(basePath: string): DiagResult | null;
9
+ export declare function registerDoctorCommand(program: Command): void;
@@ -0,0 +1,191 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import { success as themePass, warn as themeWarn, error as themeFail, heading, dim } from "../utils/theme.js";
5
+ import { isGitRepo } from "../utils/git.js";
6
+ const PASS = themePass("");
7
+ const WARN = themeWarn("");
8
+ const FAIL = themeFail("");
9
+ export function checkContentHealth(basePath) {
10
+ const results = [];
11
+ // activeContext.md: non-empty Current Focus
12
+ const acPath = join(basePath, ".context", "activeContext.md");
13
+ if (existsSync(acPath)) {
14
+ const content = readFileSync(acPath, "utf-8");
15
+ const focusMatch = content.match(/## Current Focus\s*\n+([\s\S]*?)(?=\n##|\n*$)/);
16
+ const focusBody = focusMatch?.[1]?.trim() ?? "";
17
+ if (!focusBody || focusBody.startsWith("_") || focusBody.includes("No active tasks")) {
18
+ results.push({ icon: WARN, message: "activeContext.md has no meaningful Current Focus" });
19
+ }
20
+ else {
21
+ results.push({ icon: PASS, message: "activeContext.md has an active focus" });
22
+ }
23
+ }
24
+ // systemPatterns.md: has at least one convention
25
+ const spPath = join(basePath, ".context", "systemPatterns.md");
26
+ if (existsSync(spPath)) {
27
+ const content = readFileSync(spPath, "utf-8");
28
+ const hasConvention = content.includes("- ") || content.includes("| ");
29
+ if (hasConvention) {
30
+ results.push({ icon: PASS, message: "systemPatterns.md has documented conventions" });
31
+ }
32
+ else {
33
+ results.push({ icon: WARN, message: "systemPatterns.md has no conventions documented" });
34
+ }
35
+ }
36
+ // TOCKET.md: contains payload or version keyword
37
+ const tocketPath = join(basePath, "TOCKET.md");
38
+ if (existsSync(tocketPath)) {
39
+ const content = readFileSync(tocketPath, "utf-8");
40
+ if (content.includes("payload") || content.includes("version")) {
41
+ results.push({ icon: PASS, message: "TOCKET.md contains protocol keywords" });
42
+ }
43
+ else {
44
+ results.push({ icon: WARN, message: "TOCKET.md may not be a valid protocol spec" });
45
+ }
46
+ }
47
+ // Agent configs reference .context/
48
+ const agentFiles = ["CLAUDE.md", "GEMINI.md"];
49
+ for (const af of agentFiles) {
50
+ const afPath = join(basePath, af);
51
+ if (existsSync(afPath)) {
52
+ const content = readFileSync(afPath, "utf-8");
53
+ if (content.includes(".context/") || content.includes(".context\\")) {
54
+ results.push({ icon: PASS, message: `${af} references .context/` });
55
+ }
56
+ else {
57
+ results.push({ icon: WARN, message: `${af} does not reference .context/` });
58
+ }
59
+ }
60
+ }
61
+ return results;
62
+ }
63
+ export function checkGitTracking(cwd) {
64
+ const results = [];
65
+ if (!isGitRepo(cwd)) {
66
+ results.push({ icon: WARN, message: "Not a git repository" });
67
+ return results;
68
+ }
69
+ // Check if .context/ is in .gitignore
70
+ try {
71
+ const output = execSync("git check-ignore .context/", {
72
+ cwd,
73
+ encoding: "utf-8",
74
+ }).trim();
75
+ if (output) {
76
+ results.push({ icon: FAIL, message: ".context/ is in .gitignore (should be tracked)" });
77
+ }
78
+ }
79
+ catch {
80
+ // exit code 1 = not ignored (good)
81
+ results.push({ icon: PASS, message: ".context/ is not gitignored" });
82
+ }
83
+ // Check for uncommitted .context/ changes
84
+ try {
85
+ const output = execSync("git status --porcelain .context/", {
86
+ cwd,
87
+ encoding: "utf-8",
88
+ }).trim();
89
+ if (output) {
90
+ const count = output.split("\n").length;
91
+ results.push({ icon: WARN, message: `${count} uncommitted change(s) in .context/` });
92
+ }
93
+ else {
94
+ results.push({ icon: PASS, message: ".context/ files are committed" });
95
+ }
96
+ }
97
+ catch {
98
+ // git error — skip
99
+ }
100
+ return results;
101
+ }
102
+ export function checkStaleness(basePath) {
103
+ const acPath = join(basePath, ".context", "activeContext.md");
104
+ if (!existsSync(acPath))
105
+ return null;
106
+ const stats = statSync(acPath);
107
+ const daysSinceModified = Math.floor((Date.now() - stats.mtimeMs) / (1000 * 60 * 60 * 24));
108
+ if (daysSinceModified > 7) {
109
+ return {
110
+ icon: WARN,
111
+ message: `activeContext.md last modified ${daysSinceModified} days ago (may be stale)`,
112
+ };
113
+ }
114
+ return null;
115
+ }
116
+ export function registerDoctorCommand(program) {
117
+ program
118
+ .command("doctor")
119
+ .description("Deep diagnostic of a Tocket workspace")
120
+ .action(() => {
121
+ const cwd = process.cwd();
122
+ const results = [];
123
+ let passCount = 0;
124
+ let warnCount = 0;
125
+ let failCount = 0;
126
+ console.log(heading("\nTocket Doctor\n"));
127
+ // 1. File existence (same as validate)
128
+ const contextDir = join(cwd, ".context");
129
+ if (!existsSync(contextDir)) {
130
+ results.push({ icon: FAIL, message: ".context/ directory missing" });
131
+ }
132
+ else {
133
+ results.push({ icon: PASS, message: ".context/ directory found" });
134
+ const requiredFiles = ["activeContext.md", "systemPatterns.md"];
135
+ const optionalFiles = ["techContext.md", "productContext.md", "progress.md"];
136
+ for (const f of requiredFiles) {
137
+ if (existsSync(join(contextDir, f))) {
138
+ results.push({ icon: PASS, message: `.context/${f} found` });
139
+ }
140
+ else {
141
+ results.push({ icon: FAIL, message: `.context/${f} missing (required)` });
142
+ }
143
+ }
144
+ for (const f of optionalFiles) {
145
+ if (existsSync(join(contextDir, f))) {
146
+ results.push({ icon: PASS, message: `.context/${f} found` });
147
+ }
148
+ else {
149
+ results.push({ icon: WARN, message: `.context/${f} missing (optional)` });
150
+ }
151
+ }
152
+ }
153
+ if (existsSync(join(cwd, "TOCKET.md"))) {
154
+ results.push({ icon: PASS, message: "TOCKET.md found" });
155
+ }
156
+ else {
157
+ results.push({ icon: FAIL, message: "TOCKET.md missing (required)" });
158
+ }
159
+ // 2. Content health
160
+ results.push(...checkContentHealth(cwd));
161
+ // 3. Staleness
162
+ const stale = checkStaleness(cwd);
163
+ if (stale)
164
+ results.push(stale);
165
+ // 4. Git tracking
166
+ results.push(...checkGitTracking(cwd));
167
+ // Print results
168
+ for (const r of results) {
169
+ console.log(` ${r.icon} ${r.message}`);
170
+ if (r.icon === PASS)
171
+ passCount++;
172
+ else if (r.icon === WARN)
173
+ warnCount++;
174
+ else
175
+ failCount++;
176
+ }
177
+ console.log("");
178
+ console.log(dim(` ${passCount} passed, ${warnCount} warnings, ${failCount} failures`));
179
+ console.log("");
180
+ if (failCount > 0) {
181
+ console.log(themeFail("Workspace has issues.") + " Run tocket init to scaffold missing files.");
182
+ process.exitCode = 1;
183
+ }
184
+ else if (warnCount > 0) {
185
+ console.log(themeWarn("Workspace is functional but has warnings."));
186
+ }
187
+ else {
188
+ console.log(themePass("Workspace is in great shape."));
189
+ }
190
+ });
191
+ }
@@ -0,0 +1,6 @@
1
+ import type { Command } from "commander";
2
+ /** Files created by `tocket init` that eject should remove. */
3
+ export declare const EJECT_FILES: readonly ["TOCKET.md", "CLAUDE.md", "GEMINI.md", ".cursorrules"];
4
+ /** Directories created by `tocket init` that eject should remove. */
5
+ export declare const EJECT_DIRS: readonly [".context"];
6
+ export declare function registerEjectCommand(program: Command): void;
@@ -0,0 +1,65 @@
1
+ import { confirm } from "@inquirer/prompts";
2
+ import { existsSync } from "node:fs";
3
+ import { rm } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { success, warn, info, dim } from "../utils/theme.js";
6
+ /** Files created by `tocket init` that eject should remove. */
7
+ export const EJECT_FILES = [
8
+ "TOCKET.md",
9
+ "CLAUDE.md",
10
+ "GEMINI.md",
11
+ ".cursorrules",
12
+ ];
13
+ /** Directories created by `tocket init` that eject should remove. */
14
+ export const EJECT_DIRS = [".context"];
15
+ export function registerEjectCommand(program) {
16
+ program
17
+ .command("eject")
18
+ .description("Remove all Tocket files from the current workspace")
19
+ .option("-f, --force", "Skip confirmation prompt")
20
+ .action(async (options) => {
21
+ const cwd = process.cwd();
22
+ const contextDir = join(cwd, ".context");
23
+ if (!existsSync(contextDir)) {
24
+ console.log(warn("No Tocket workspace found in this directory."));
25
+ return;
26
+ }
27
+ if (!options.force) {
28
+ const ok = await confirm({
29
+ message: "This will permanently remove .context/, CLAUDE.md, GEMINI.md, TOCKET.md, and .cursorrules. Continue?",
30
+ default: false,
31
+ });
32
+ if (!ok) {
33
+ console.log(dim("\n Cancelled.\n"));
34
+ return;
35
+ }
36
+ }
37
+ let removedCount = 0;
38
+ for (const dir of EJECT_DIRS) {
39
+ const fullPath = join(cwd, dir);
40
+ if (existsSync(fullPath)) {
41
+ await rm(fullPath, { recursive: true, force: true });
42
+ console.log(info(`Removed ${dir}/`));
43
+ removedCount++;
44
+ }
45
+ }
46
+ for (const file of EJECT_FILES) {
47
+ const fullPath = join(cwd, file);
48
+ if (existsSync(fullPath)) {
49
+ await rm(fullPath, { force: true });
50
+ console.log(info(`Removed ${file}`));
51
+ removedCount++;
52
+ }
53
+ }
54
+ if (removedCount === 0) {
55
+ console.log(warn("Nothing to remove."));
56
+ }
57
+ else {
58
+ console.log("\n" +
59
+ success("Tocket workspace ejected.") +
60
+ " " +
61
+ dim("Global config (~/.tocketrc.json) was not touched.") +
62
+ "\n");
63
+ }
64
+ });
65
+ }
@@ -0,0 +1,7 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Replaces the content under ## Current Focus with the new message.
4
+ * Exported for testing.
5
+ */
6
+ export declare function replaceFocusSection(content: string, newFocus: string): string;
7
+ export declare function registerFocusCommand(program: Command): void;