speclock 1.1.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/LICENSE +21 -0
- package/README.md +289 -0
- package/bin/speclock.js +2 -0
- package/package.json +59 -0
- package/src/cli/index.js +285 -0
- package/src/core/context.js +201 -0
- package/src/core/engine.js +698 -0
- package/src/core/git.js +110 -0
- package/src/core/storage.js +186 -0
- package/src/mcp/server.js +730 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sandeep Roy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# SpecLock
|
|
2
|
+
|
|
3
|
+
**AI Continuity Engine** — The MCP server that kills AI amnesia.
|
|
4
|
+
|
|
5
|
+
> Developed by **Sandeep Roy** ([github.com/sgroy10](https://github.com/sgroy10))
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/speclock)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://modelcontextprotocol.io)
|
|
10
|
+
[](https://nodejs.org)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Problem
|
|
15
|
+
|
|
16
|
+
Every AI coding tool forgets. Every. Single. Session.
|
|
17
|
+
|
|
18
|
+
- Claude Code forgets the decisions you made yesterday
|
|
19
|
+
- Cursor forgets the constraints you set last week
|
|
20
|
+
- Codex rebuilds what another agent already built
|
|
21
|
+
- Your AI agent violates rules it agreed to 3 sessions ago
|
|
22
|
+
|
|
23
|
+
**AI amnesia is the #1 productivity killer in AI-assisted development.**
|
|
24
|
+
|
|
25
|
+
## The Solution
|
|
26
|
+
|
|
27
|
+
SpecLock maintains a `.speclock/` directory inside your repo that gives every AI agent perfect memory — across sessions, across tools, across time.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
.speclock/
|
|
31
|
+
├── brain.json # Structured project memory
|
|
32
|
+
├── events.log # Append-only event ledger (JSONL)
|
|
33
|
+
├── patches/ # Git diffs captured per event
|
|
34
|
+
└── context/
|
|
35
|
+
└── latest.md # Always-fresh context pack for any AI agent
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Any AI tool calls `speclock_session_briefing` → gets **full project context** → works with complete memory → calls `speclock_session_summary` → next session continues seamlessly.
|
|
39
|
+
|
|
40
|
+
**No context is ever lost again.**
|
|
41
|
+
|
|
42
|
+
## Why SpecLock Wins
|
|
43
|
+
|
|
44
|
+
| Feature | CLAUDE.md / .cursorrules | Chat History | Memory Plugins | **SpecLock** |
|
|
45
|
+
|---------|--------------------------|--------------|----------------|--------------|
|
|
46
|
+
| Structured memory | Static files | Noise-heavy | Generic | **Structured brain.json** |
|
|
47
|
+
| Constraint enforcement | None | None | None | **Active lock checking** |
|
|
48
|
+
| Conflict detection | None | None | None | **Semantic + synonym matching** |
|
|
49
|
+
| Drift detection | None | None | None | **Auto-scan against locks** |
|
|
50
|
+
| Git-aware | No | No | No | **Checkpoints, diffs, reverts** |
|
|
51
|
+
| Multi-agent | No | No | Partial | **Full session timeline** |
|
|
52
|
+
| Auto-suggestions | No | No | No | **AI-powered lock suggestions** |
|
|
53
|
+
| Cross-tool | Tool-specific | Tool-specific | Tool-specific | **Universal MCP** |
|
|
54
|
+
|
|
55
|
+
**Other tools remember. SpecLock enforces.**
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
### 1. Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install -g speclock
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or use directly with npx:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx speclock init
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. Initialize in Your Project
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd your-project
|
|
75
|
+
speclock init
|
|
76
|
+
speclock goal "Ship v1 of the product"
|
|
77
|
+
speclock lock "No breaking changes to public API"
|
|
78
|
+
speclock decide "Use PostgreSQL for persistence"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. Connect to Your AI Tool
|
|
82
|
+
|
|
83
|
+
**Claude Code** — Add to `.claude/settings.json`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"mcpServers": {
|
|
88
|
+
"speclock": {
|
|
89
|
+
"command": "npx",
|
|
90
|
+
"args": ["-y", "speclock", "serve", "--project", "."]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Cursor** — Add to `.cursor/mcp.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"speclock": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["-y", "speclock", "serve", "--project", "."]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Windsurf / Cline / Any MCP tool** — Same pattern:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"mcpServers": {
|
|
114
|
+
"speclock": {
|
|
115
|
+
"command": "npx",
|
|
116
|
+
"args": ["-y", "speclock", "serve", "--project", "."]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. Use It
|
|
123
|
+
|
|
124
|
+
The AI tool now has access to **19 SpecLock tools**. The key workflow:
|
|
125
|
+
|
|
126
|
+
1. **Start session**: AI calls `speclock_session_briefing` — gets full context + what changed
|
|
127
|
+
2. **During work**: AI uses `add_decision`, `log_change`, `check_conflict`, `detect_drift`
|
|
128
|
+
3. **End session**: AI calls `speclock_session_summary` — records what was accomplished
|
|
129
|
+
4. **Next session (any tool)**: Step 1 repeats — full continuity preserved
|
|
130
|
+
|
|
131
|
+
## MCP Tools (19)
|
|
132
|
+
|
|
133
|
+
### Memory Management
|
|
134
|
+
| Tool | Purpose |
|
|
135
|
+
|------|---------|
|
|
136
|
+
| `speclock_init` | Initialize SpecLock in project |
|
|
137
|
+
| `speclock_get_context` | **THE KEY TOOL** — full context pack |
|
|
138
|
+
| `speclock_set_goal` | Set/update project goal |
|
|
139
|
+
| `speclock_add_lock` | Add non-negotiable constraint |
|
|
140
|
+
| `speclock_remove_lock` | Deactivate a lock by ID |
|
|
141
|
+
| `speclock_add_decision` | Record an architectural decision |
|
|
142
|
+
| `speclock_add_note` | Add a pinned note |
|
|
143
|
+
| `speclock_set_deploy_facts` | Record deploy configuration |
|
|
144
|
+
|
|
145
|
+
### Change Tracking
|
|
146
|
+
| Tool | Purpose |
|
|
147
|
+
|------|---------|
|
|
148
|
+
| `speclock_log_change` | Manually log a significant change |
|
|
149
|
+
| `speclock_get_changes` | Get recent tracked changes |
|
|
150
|
+
| `speclock_get_events` | Get event log (filterable by type/time) |
|
|
151
|
+
|
|
152
|
+
### Continuity Protection
|
|
153
|
+
| Tool | Purpose |
|
|
154
|
+
|------|---------|
|
|
155
|
+
| `speclock_check_conflict` | Check action against locks (semantic matching) |
|
|
156
|
+
| `speclock_session_briefing` | Start session + full briefing |
|
|
157
|
+
| `speclock_session_summary` | End session + record summary |
|
|
158
|
+
|
|
159
|
+
### Git Integration
|
|
160
|
+
| Tool | Purpose |
|
|
161
|
+
|------|---------|
|
|
162
|
+
| `speclock_checkpoint` | Create named git tag for rollback |
|
|
163
|
+
| `speclock_repo_status` | Branch, commit, changed files, diff |
|
|
164
|
+
|
|
165
|
+
### Intelligence (NEW in v1.1)
|
|
166
|
+
| Tool | Purpose |
|
|
167
|
+
|------|---------|
|
|
168
|
+
| `speclock_suggest_locks` | AI-powered lock suggestions from patterns |
|
|
169
|
+
| `speclock_detect_drift` | Scan changes for constraint violations |
|
|
170
|
+
| `speclock_health` | Health score + multi-agent timeline |
|
|
171
|
+
|
|
172
|
+
## Killer Features
|
|
173
|
+
|
|
174
|
+
### Semantic Conflict Detection
|
|
175
|
+
|
|
176
|
+
Not just keyword matching — SpecLock understands synonyms, negation, and destructive intent:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Lock: "No breaking changes to public API"
|
|
180
|
+
|
|
181
|
+
Action: "remove the external endpoints"
|
|
182
|
+
Result: [HIGH] Conflict detected (confidence: 85%)
|
|
183
|
+
- synonym match: remove/delete, external/public, endpoints/api
|
|
184
|
+
- lock prohibits this action (negation detected)
|
|
185
|
+
- destructive action against locked constraint
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Auto-Lock Suggestions
|
|
189
|
+
|
|
190
|
+
SpecLock analyzes your decisions and notes for commitment language and suggests constraints:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Decision: "Always use REST for public endpoints"
|
|
194
|
+
→ Suggestion: Promote to lock (contains "always" — strong commitment language)
|
|
195
|
+
|
|
196
|
+
Project mentions "security" but has no security lock
|
|
197
|
+
→ Suggestion: "No secrets or credentials in source code"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Drift Detection
|
|
201
|
+
|
|
202
|
+
Proactively scans recent changes against your locks:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Lock: "No database schema changes without migration"
|
|
206
|
+
Change: "Modified users table schema directly"
|
|
207
|
+
→ [HIGH] Drift detected — review immediately
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Multi-Agent Timeline
|
|
211
|
+
|
|
212
|
+
Track which AI tools touched your project and what they did:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
Health Check — Score: 85/100 (Grade: A)
|
|
216
|
+
|
|
217
|
+
Multi-Agent Timeline:
|
|
218
|
+
- claude-code: 12 sessions, last active 2026-02-24
|
|
219
|
+
- cursor: 5 sessions, last active 2026-02-23
|
|
220
|
+
- codex: 2 sessions, last active 2026-02-20
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## CLI Commands
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
speclock init Initialize SpecLock
|
|
227
|
+
speclock goal <text> Set project goal
|
|
228
|
+
speclock lock <text> [--tags a,b] Add a SpecLock constraint
|
|
229
|
+
speclock lock remove <id> Remove a lock
|
|
230
|
+
speclock decide <text> Record a decision
|
|
231
|
+
speclock note <text> Add a note
|
|
232
|
+
speclock facts deploy --provider X Set deploy facts
|
|
233
|
+
speclock context Generate and print context pack
|
|
234
|
+
speclock watch Start file watcher
|
|
235
|
+
speclock serve [--project <path>] Start MCP server
|
|
236
|
+
speclock status Show brain summary
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Why MCP?
|
|
240
|
+
|
|
241
|
+
MCP (Model Context Protocol) is the universal integration standard for AI tools. One SpecLock MCP server works with:
|
|
242
|
+
|
|
243
|
+
- Claude Code
|
|
244
|
+
- Cursor
|
|
245
|
+
- Windsurf
|
|
246
|
+
- Cline
|
|
247
|
+
- Codex
|
|
248
|
+
- Any MCP-compatible tool
|
|
249
|
+
|
|
250
|
+
SpecLock is infrastructure, not a competitor. It makes **every** AI coding tool better.
|
|
251
|
+
|
|
252
|
+
## Architecture
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
┌─────────────────────────────────────────────────────────┐
|
|
256
|
+
│ AI Tool (Claude Code, Cursor, etc.) │
|
|
257
|
+
└────────────────────┬────────────────────────────────────┘
|
|
258
|
+
│ MCP Protocol (stdio)
|
|
259
|
+
┌────────────────────▼────────────────────────────────────┐
|
|
260
|
+
│ SpecLock MCP Server (19 tools) │
|
|
261
|
+
│ Memory | Tracking | Protection | Git | Intelligence │
|
|
262
|
+
└────────────────────┬────────────────────────────────────┘
|
|
263
|
+
│
|
|
264
|
+
.speclock/
|
|
265
|
+
├── brain.json (structured memory)
|
|
266
|
+
├── events.log (immutable audit trail)
|
|
267
|
+
├── patches/ (git diffs per event)
|
|
268
|
+
└── context/ (generated context packs)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Contributing
|
|
272
|
+
|
|
273
|
+
Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/sgroy10/speclock).
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT License - see [LICENSE](LICENSE) file.
|
|
278
|
+
|
|
279
|
+
## Author
|
|
280
|
+
|
|
281
|
+
**Developed by Sandeep Roy**
|
|
282
|
+
|
|
283
|
+
- GitHub: [github.com/sgroy10](https://github.com/sgroy10)
|
|
284
|
+
- Repository: [github.com/sgroy10/speclock](https://github.com/sgroy10/speclock)
|
|
285
|
+
- npm: [npmjs.com/package/speclock](https://www.npmjs.com/package/speclock)
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
*SpecLock — Because no AI session should ever forget.*
|
package/bin/speclock.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "speclock",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "AI continuity engine — MCP server + CLI that kills AI amnesia. Maintains project memory, enforces constraints, and detects drift across AI coding sessions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/mcp/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"speclock": "./bin/speclock.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/mcp/server.js",
|
|
12
|
+
"serve": "node src/mcp/server.js",
|
|
13
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"mcp-server",
|
|
18
|
+
"ai",
|
|
19
|
+
"ai-memory",
|
|
20
|
+
"ai-continuity",
|
|
21
|
+
"context",
|
|
22
|
+
"memory",
|
|
23
|
+
"claude",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"cursor",
|
|
26
|
+
"codex",
|
|
27
|
+
"windsurf",
|
|
28
|
+
"cline",
|
|
29
|
+
"speclock",
|
|
30
|
+
"ai-amnesia",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"drift-detection",
|
|
33
|
+
"constraint-enforcement"
|
|
34
|
+
],
|
|
35
|
+
"author": "Sandeep Roy (https://github.com/sgroy10)",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"homepage": "https://github.com/sgroy10/speclock#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/sgroy10/speclock/issues"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/sgroy10/speclock.git"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
50
|
+
"chokidar": "^3.6.0",
|
|
51
|
+
"zod": "^3.25.0"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"bin/",
|
|
55
|
+
"src/",
|
|
56
|
+
"README.md",
|
|
57
|
+
"LICENSE"
|
|
58
|
+
]
|
|
59
|
+
}
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import {
|
|
3
|
+
ensureInit,
|
|
4
|
+
setGoal,
|
|
5
|
+
addLock,
|
|
6
|
+
removeLock,
|
|
7
|
+
addDecision,
|
|
8
|
+
addNote,
|
|
9
|
+
updateDeployFacts,
|
|
10
|
+
watchRepo,
|
|
11
|
+
} from "../core/engine.js";
|
|
12
|
+
import { generateContext } from "../core/context.js";
|
|
13
|
+
import { readBrain } from "../core/storage.js";
|
|
14
|
+
|
|
15
|
+
// --- Argument parsing ---
|
|
16
|
+
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const args = argv.slice(2);
|
|
19
|
+
const cmd = args.shift() || "";
|
|
20
|
+
return { cmd, args };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseFlags(args) {
|
|
24
|
+
const out = { _: [] };
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const a = args[i];
|
|
27
|
+
if (a.startsWith("--")) {
|
|
28
|
+
const key = a.slice(2);
|
|
29
|
+
const next = args[i + 1];
|
|
30
|
+
if (!next || next.startsWith("--")) {
|
|
31
|
+
out[key] = true;
|
|
32
|
+
} else {
|
|
33
|
+
out[key] = next;
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
out._.push(a);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseTags(raw) {
|
|
44
|
+
if (!raw) return [];
|
|
45
|
+
return raw
|
|
46
|
+
.split(",")
|
|
47
|
+
.map((t) => t.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function rootDir() {
|
|
52
|
+
return process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- Help text ---
|
|
56
|
+
|
|
57
|
+
function printHelp() {
|
|
58
|
+
console.log(`
|
|
59
|
+
SpecLock v1.1.0 — AI Continuity Engine
|
|
60
|
+
Developed by Sandeep Roy (github.com/sgroy10)
|
|
61
|
+
|
|
62
|
+
Usage: speclock <command> [options]
|
|
63
|
+
|
|
64
|
+
Commands:
|
|
65
|
+
init Initialize SpecLock in current directory
|
|
66
|
+
goal <text> Set or update the project goal
|
|
67
|
+
lock <text> [--tags a,b] Add a non-negotiable constraint (SpecLock)
|
|
68
|
+
lock remove <id> Remove a lock by ID
|
|
69
|
+
decide <text> [--tags a,b] Record a decision
|
|
70
|
+
note <text> [--pinned] Add a pinned note
|
|
71
|
+
facts deploy [--provider X] Set deployment facts
|
|
72
|
+
context Generate and print context pack
|
|
73
|
+
watch Start file watcher (auto-track changes)
|
|
74
|
+
serve [--project <path>] Start MCP stdio server
|
|
75
|
+
status Show project brain summary
|
|
76
|
+
|
|
77
|
+
Options:
|
|
78
|
+
--tags <a,b,c> Comma-separated tags
|
|
79
|
+
--source <user|agent> Who created this (default: user)
|
|
80
|
+
--provider <name> Deploy provider
|
|
81
|
+
--branch <name> Deploy branch
|
|
82
|
+
--autoDeploy <true|false> Auto-deploy setting
|
|
83
|
+
--url <url> Deployment URL
|
|
84
|
+
--notes <text> Additional notes
|
|
85
|
+
--project <path> Project root (for serve)
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
speclock init
|
|
89
|
+
speclock goal "Ship v1 of the continuity engine"
|
|
90
|
+
speclock lock "No external database in v1" --tags scope
|
|
91
|
+
speclock decide "Use MCP as primary integration" --tags architecture
|
|
92
|
+
speclock context
|
|
93
|
+
speclock serve --project /path/to/repo
|
|
94
|
+
|
|
95
|
+
MCP Tools (19): init, get_context, set_goal, add_lock, remove_lock,
|
|
96
|
+
add_decision, add_note, set_deploy_facts, log_change, get_changes,
|
|
97
|
+
get_events, check_conflict, session_briefing, session_summary,
|
|
98
|
+
checkpoint, repo_status, suggest_locks, detect_drift, health
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// --- Status display ---
|
|
103
|
+
|
|
104
|
+
function showStatus(root) {
|
|
105
|
+
const brain = readBrain(root);
|
|
106
|
+
if (!brain) {
|
|
107
|
+
console.log("SpecLock not initialized. Run: speclock init");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const activeLocks = brain.specLock.items.filter((l) => l.active !== false);
|
|
112
|
+
|
|
113
|
+
console.log(`\nSpecLock Status — ${brain.project.name}`);
|
|
114
|
+
console.log("=".repeat(50));
|
|
115
|
+
console.log(`Goal: ${brain.goal.text || "(not set)"}`);
|
|
116
|
+
console.log(`SpecLocks: ${activeLocks.length} active`);
|
|
117
|
+
console.log(`Decisions: ${brain.decisions.length}`);
|
|
118
|
+
console.log(`Notes: ${brain.notes.length}`);
|
|
119
|
+
console.log(`Events: ${brain.events.count}`);
|
|
120
|
+
console.log(`Deploy: ${brain.facts.deploy.provider}`);
|
|
121
|
+
|
|
122
|
+
if (brain.sessions.current) {
|
|
123
|
+
console.log(`Session: active (${brain.sessions.current.toolUsed})`);
|
|
124
|
+
} else {
|
|
125
|
+
console.log(`Session: none active`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (brain.sessions.history.length > 0) {
|
|
129
|
+
const last = brain.sessions.history[0];
|
|
130
|
+
console.log(
|
|
131
|
+
`Last session: ${last.toolUsed} — ${last.summary || "(no summary)"}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`Recent changes: ${brain.state.recentChanges.length}`);
|
|
136
|
+
console.log(`Reverts: ${brain.state.reverts.length}`);
|
|
137
|
+
console.log("");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// --- Main ---
|
|
141
|
+
|
|
142
|
+
async function main() {
|
|
143
|
+
const { cmd, args } = parseArgs(process.argv);
|
|
144
|
+
const root = rootDir();
|
|
145
|
+
|
|
146
|
+
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
147
|
+
printHelp();
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (cmd === "init") {
|
|
152
|
+
ensureInit(root);
|
|
153
|
+
console.log("SpecLock initialized.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (cmd === "goal") {
|
|
158
|
+
const text = args.join(" ").trim();
|
|
159
|
+
if (!text) {
|
|
160
|
+
console.error("Error: Goal text is required.");
|
|
161
|
+
console.error("Usage: speclock goal <text>");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
setGoal(root, text);
|
|
165
|
+
console.log(`Goal set: "${text}"`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (cmd === "lock") {
|
|
170
|
+
// Check for "lock remove <id>"
|
|
171
|
+
if (args[0] === "remove") {
|
|
172
|
+
const lockId = args[1];
|
|
173
|
+
if (!lockId) {
|
|
174
|
+
console.error("Error: Lock ID is required.");
|
|
175
|
+
console.error("Usage: speclock lock remove <lockId>");
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
const result = removeLock(root, lockId);
|
|
179
|
+
if (result.removed) {
|
|
180
|
+
console.log(`Lock removed: "${result.lockText}"`);
|
|
181
|
+
} else {
|
|
182
|
+
console.error(result.error);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const flags = parseFlags(args);
|
|
189
|
+
const text = flags._.join(" ").trim();
|
|
190
|
+
if (!text) {
|
|
191
|
+
console.error("Error: Lock text is required.");
|
|
192
|
+
console.error("Usage: speclock lock <text> [--tags a,b] [--source user]");
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
const { lockId } = addLock(root, text, parseTags(flags.tags), flags.source || "user");
|
|
196
|
+
console.log(`SpecLock added (${lockId}): "${text}"`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (cmd === "decide") {
|
|
201
|
+
const flags = parseFlags(args);
|
|
202
|
+
const text = flags._.join(" ").trim();
|
|
203
|
+
if (!text) {
|
|
204
|
+
console.error("Error: Decision text is required.");
|
|
205
|
+
console.error("Usage: speclock decide <text> [--tags a,b]");
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
const { decId } = addDecision(root, text, parseTags(flags.tags), flags.source || "user");
|
|
209
|
+
console.log(`Decision recorded (${decId}): "${text}"`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (cmd === "note") {
|
|
214
|
+
const flags = parseFlags(args);
|
|
215
|
+
const text = flags._.join(" ").trim();
|
|
216
|
+
if (!text) {
|
|
217
|
+
console.error("Error: Note text is required.");
|
|
218
|
+
console.error("Usage: speclock note <text> [--pinned]");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
const pinned = flags.pinned !== false;
|
|
222
|
+
const { noteId } = addNote(root, text, pinned);
|
|
223
|
+
console.log(`Note added (${noteId}): "${text}"`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (cmd === "facts") {
|
|
228
|
+
const sub = args.shift();
|
|
229
|
+
if (sub !== "deploy") {
|
|
230
|
+
console.error("Error: Only 'facts deploy' is supported.");
|
|
231
|
+
console.error(
|
|
232
|
+
"Usage: speclock facts deploy --provider X --branch Y"
|
|
233
|
+
);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const flags = parseFlags(args);
|
|
237
|
+
const payload = {
|
|
238
|
+
provider: flags.provider,
|
|
239
|
+
branch: flags.branch,
|
|
240
|
+
notes: flags.notes,
|
|
241
|
+
url: flags.url,
|
|
242
|
+
};
|
|
243
|
+
if (flags.autoDeploy !== undefined) {
|
|
244
|
+
payload.autoDeploy =
|
|
245
|
+
String(flags.autoDeploy).toLowerCase() === "true";
|
|
246
|
+
}
|
|
247
|
+
updateDeployFacts(root, payload);
|
|
248
|
+
console.log("Deploy facts updated.");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (cmd === "context") {
|
|
253
|
+
const md = generateContext(root);
|
|
254
|
+
console.log(md);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (cmd === "watch") {
|
|
259
|
+
await watchRepo(root);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (cmd === "serve") {
|
|
264
|
+
// Start MCP server — pass through --project if provided
|
|
265
|
+
const flags = parseFlags(args);
|
|
266
|
+
const projectArg = flags.project || root;
|
|
267
|
+
process.env.SPECLOCK_PROJECT_ROOT = projectArg;
|
|
268
|
+
await import("../mcp/server.js");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (cmd === "status") {
|
|
273
|
+
showStatus(root);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.error(`Unknown command: ${cmd}`);
|
|
278
|
+
console.error("Run 'speclock --help' for usage.");
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
main().catch((err) => {
|
|
283
|
+
console.error("SpecLock error:", err.message);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
});
|