@openlap/openlap 1.0.0 → 1.0.1
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 +78 -0
- package/dist/feed.d.ts +2 -1
- package/dist/feed.js +9 -2
- package/dist/proxy.js +13 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# openlap
|
|
2
|
+
|
|
3
|
+
Product requirements that agents read. Write a lap, agent builds it.
|
|
4
|
+
|
|
5
|
+
A lap is one goal, issues to implement, and verification criteria. Agents pick the next open lap and work through it. When they commit, criteria auto-check via webhook. No dashboard, no CLI -- just the MCP server.
|
|
6
|
+
|
|
7
|
+
Live at [openlap.app](https://openlap.app)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
1. Admin installs the GitHub App (once per org): https://github.com/apps/openlap-app/installations/new
|
|
12
|
+
2. Add the MCP server:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
claude mcp add --transport http lap https://openlap.app/mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
3. Open Claude Code. First message triggers GitHub login in browser. Done.
|
|
19
|
+
|
|
20
|
+
For project-scoped briefings (filters context to one repo):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
claude mcp add --transport http --scope project lap "https://openlap.app/mcp?project=owner/repo"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Project scope overrides global when both exist.
|
|
27
|
+
|
|
28
|
+
4. Register your repo as a project (once per repo):
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
create_project owner/repo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## What agents see
|
|
35
|
+
|
|
36
|
+
**Briefings** -- computed context injected into tool descriptions. Agents see project health, the focused lap's unchecked criteria, staleness signals, and priority collisions without calling any tool. Briefings refresh mid-session after mutations.
|
|
37
|
+
|
|
38
|
+
**Laps** -- sorted by priority (0=focus, 1=urgent, 2=high, 3=normal). Agents work on the first open lap. Each lap has a goal, body, and structured criteria.
|
|
39
|
+
|
|
40
|
+
**Criteria** -- verification checks on each lap. Agents prove work by committing with `LAP-NNN #N` in the commit message:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git commit -m "LAP-010 #1 #2 add auth middleware"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The GitHub webhook auto-checks the referenced criteria. When all code criteria pass, the lap auto-closes to done. If manual criteria remain (screenshots, design review), the lap moves to review for human verification.
|
|
47
|
+
|
|
48
|
+
**Focus mode** -- set priority=0 on one lap per project. Its unchecked criteria appear in briefings automatically, so agents know what to verify from `tools/list` alone.
|
|
49
|
+
|
|
50
|
+
## Develop
|
|
51
|
+
|
|
52
|
+
Prerequisites: Go 1.23+, a GitHub App.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
export BASE_URL=http://localhost:7784
|
|
56
|
+
export DB_PATH=./openlap.db
|
|
57
|
+
cd cmd/anylap && go run .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Local MCP (overrides the remote server):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
claude mcp add --transport http lap http://localhost:7784/mcp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Deploy
|
|
67
|
+
|
|
68
|
+
Hetzner. `sky ship --app openlap-1`. Secrets via `sky deploy --app openlap-1`.
|
|
69
|
+
|
|
70
|
+
## Architecture
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
Agent -> MCP (openlap.app/mcp) -> SQLite
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
One Go binary serves REST API + MCP protocol. OAuth 2.1 with PKCE, chaining to GitHub for identity. GitHub App installation = team boundary. SQLite with Litestream S3 backup.
|
|
77
|
+
|
|
78
|
+
See CLAUDE.md for technical reference.
|
package/dist/feed.d.ts
CHANGED
|
@@ -11,7 +11,8 @@ export declare class FeedManager {
|
|
|
11
11
|
private streams;
|
|
12
12
|
private seenIds;
|
|
13
13
|
private callback;
|
|
14
|
-
|
|
14
|
+
private getToken;
|
|
15
|
+
constructor(baseUrl: string, callback: UpdateCallback, getToken?: () => string | undefined);
|
|
15
16
|
/**
|
|
16
17
|
* Subscribe to a tag's feed if not already subscribed.
|
|
17
18
|
*/
|
package/dist/feed.js
CHANGED
|
@@ -5,9 +5,11 @@ export class FeedManager {
|
|
|
5
5
|
streams = new Map();
|
|
6
6
|
seenIds = new Set();
|
|
7
7
|
callback;
|
|
8
|
-
|
|
8
|
+
getToken;
|
|
9
|
+
constructor(baseUrl, callback, getToken) {
|
|
9
10
|
this.baseUrl = baseUrl;
|
|
10
11
|
this.callback = callback;
|
|
12
|
+
this.getToken = getToken;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Subscribe to a tag's feed if not already subscribed.
|
|
@@ -22,7 +24,12 @@ export class FeedManager {
|
|
|
22
24
|
}
|
|
23
25
|
connect(tag, controller) {
|
|
24
26
|
const url = `${this.baseUrl}/feed/${tag}/sse`;
|
|
25
|
-
|
|
27
|
+
const headers = {};
|
|
28
|
+
const token = this.getToken?.();
|
|
29
|
+
if (token) {
|
|
30
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
31
|
+
}
|
|
32
|
+
fetch(url, { headers, signal: controller.signal })
|
|
26
33
|
.then((res) => {
|
|
27
34
|
if (!res.ok || !res.body) {
|
|
28
35
|
throw new Error(`SSE connect failed: ${res.status}`);
|
package/dist/proxy.js
CHANGED
|
@@ -3,10 +3,13 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { homedir } from "os";
|
|
6
9
|
import { FileOAuthProvider } from "./auth.js";
|
|
7
10
|
import { detectProject, autoSave } from "./git.js";
|
|
8
11
|
import { FeedManager } from "./feed.js";
|
|
9
|
-
const BASE_URL = process.env.
|
|
12
|
+
const BASE_URL = process.env.OPENLAP_URL ?? "https://openlap.app";
|
|
10
13
|
// Tools that accept a 'project' parameter
|
|
11
14
|
const PROJECT_TOOLS = new Set([
|
|
12
15
|
"list_laps", "get_lap", "create_lap", "save_lap", "update_lap",
|
|
@@ -70,6 +73,15 @@ export async function startProxy() {
|
|
|
70
73
|
process.stderr.write(`[openlap] notification error: ${err}\n`);
|
|
71
74
|
}
|
|
72
75
|
});
|
|
76
|
+
}, () => {
|
|
77
|
+
// Provide auth token for SSE feed connections
|
|
78
|
+
try {
|
|
79
|
+
const data = JSON.parse(readFileSync(join(homedir(), ".openlap", "auth.json"), "utf-8"));
|
|
80
|
+
return data?.tokens?.access_token;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
73
85
|
});
|
|
74
86
|
// Forward tool listing from remote
|
|
75
87
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|