intent-cli-python 1.1.0__tar.gz → 1.3.0__tar.gz

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 (42) hide show
  1. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/PKG-INFO +23 -1
  2. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/README.md +22 -0
  3. intent_cli_python-1.3.0/apps/__init__.py +1 -0
  4. intent_cli_python-1.3.0/apps/inthub_api/__init__.py +1 -0
  5. intent_cli_python-1.3.0/apps/inthub_api/__main__.py +4 -0
  6. intent_cli_python-1.3.0/apps/inthub_api/common.py +43 -0
  7. intent_cli_python-1.3.0/apps/inthub_api/db.py +47 -0
  8. intent_cli_python-1.3.0/apps/inthub_api/ingest.py +170 -0
  9. intent_cli_python-1.3.0/apps/inthub_api/queries.py +366 -0
  10. intent_cli_python-1.3.0/apps/inthub_api/server.py +168 -0
  11. intent_cli_python-1.3.0/apps/inthub_api/store.py +31 -0
  12. intent_cli_python-1.3.0/apps/inthub_web/__init__.py +1 -0
  13. intent_cli_python-1.3.0/apps/inthub_web/__main__.py +4 -0
  14. intent_cli_python-1.3.0/apps/inthub_web/server.py +87 -0
  15. intent_cli_python-1.3.0/apps/inthub_web/static/app.js +745 -0
  16. intent_cli_python-1.3.0/apps/inthub_web/static/index.html +123 -0
  17. intent_cli_python-1.3.0/apps/inthub_web/static/styles.css +490 -0
  18. {intent_cli_python-1.1.0/src → intent_cli_python-1.3.0}/intent_cli_python.egg-info/PKG-INFO +23 -1
  19. intent_cli_python-1.3.0/intent_cli_python.egg-info/SOURCES.txt +37 -0
  20. intent_cli_python-1.3.0/intent_cli_python.egg-info/entry_points.txt +4 -0
  21. {intent_cli_python-1.1.0/src → intent_cli_python-1.3.0}/intent_cli_python.egg-info/top_level.txt +1 -0
  22. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/pyproject.toml +8 -5
  23. intent_cli_python-1.3.0/src/intent_cli/cli.py +178 -0
  24. intent_cli_python-1.3.0/src/intent_cli/commands/__init__.py +1 -0
  25. intent_cli_python-1.3.0/src/intent_cli/commands/common.py +41 -0
  26. intent_cli_python-1.3.0/src/intent_cli/commands/core.py +376 -0
  27. intent_cli_python-1.3.0/src/intent_cli/commands/hub.py +107 -0
  28. intent_cli_python-1.3.0/src/intent_cli/hub/__init__.py +1 -0
  29. intent_cli_python-1.3.0/src/intent_cli/hub/client.py +59 -0
  30. intent_cli_python-1.3.0/src/intent_cli/hub/payload.py +65 -0
  31. intent_cli_python-1.3.0/src/intent_cli/hub/runtime.py +40 -0
  32. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/src/intent_cli/store.py +96 -0
  33. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/tests/test_cli.py +138 -0
  34. intent_cli_python-1.1.0/src/intent_cli/cli.py +0 -528
  35. intent_cli_python-1.1.0/src/intent_cli_python.egg-info/SOURCES.txt +0 -14
  36. intent_cli_python-1.1.0/src/intent_cli_python.egg-info/entry_points.txt +0 -2
  37. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/LICENSE +0 -0
  38. {intent_cli_python-1.1.0/src → intent_cli_python-1.3.0}/intent_cli_python.egg-info/dependency_links.txt +0 -0
  39. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/setup.cfg +0 -0
  40. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/src/intent_cli/__init__.py +0 -0
  41. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/src/intent_cli/__main__.py +0 -0
  42. {intent_cli_python-1.1.0 → intent_cli_python-1.3.0}/src/intent_cli/output.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: intent-cli-python
3
- Version: 1.1.0
3
+ Version: 1.3.0
4
4
  Summary: Semantic history for agent-driven development. Records what you did and why.
5
5
  Author: Zeng Deyang
6
6
  License-Expression: MIT
@@ -72,6 +72,24 @@ pip install intent-cli-python
72
72
 
73
73
  Requires Python 3.9+ and Git.
74
74
 
75
+ ### Run the local IntHub shell
76
+
77
+ The first read-only IntHub shell now ships with the package:
78
+
79
+ ```bash
80
+ inthub-api --db-path .inthub/inthub.db
81
+ inthub-web --api-base-url http://127.0.0.1:8000
82
+ ```
83
+
84
+ If you are running from source instead of an installed package, the same entrypoints are available with:
85
+
86
+ ```bash
87
+ python -m apps.inthub_api --db-path .inthub/inthub.db
88
+ python -m apps.inthub_web --api-base-url http://127.0.0.1:8000
89
+ ```
90
+
91
+ Then use `itt hub login`, `itt hub link`, and `itt hub sync` from a local Intent workspace to populate the read-only IntHub project view.
92
+
75
93
  ### Install the skills.sh skill
76
94
 
77
95
  ```bash
@@ -173,11 +191,15 @@ All data lives in `.intent/` at your git repo root:
173
191
  decision-001.json
174
192
  ```
175
193
 
194
+ `.intent/` is local semantic-workspace metadata. It should stay out of Git history and should remain ignored by `.gitignore`.
195
+
176
196
  ## Docs
177
197
 
178
198
  - [Vision](docs/EN/vision.md) — why semantic history matters. **If this project interests you, start here.**
179
199
  - [CLI Design](docs/EN/cli.md) — object model, commands, JSON contract
180
200
  - [Roadmap](docs/EN/roadmap.md) — phase plan
201
+ - [IntHub MVP](docs/EN/inthub-mvp.md) — first remote collaboration-layer scope
202
+ - [IntHub Sync Contract](docs/EN/inthub-sync-contract.md) — first sync, identity, and API contract
181
203
 
182
204
  ## License
183
205
 
@@ -48,6 +48,24 @@ pip install intent-cli-python
48
48
 
49
49
  Requires Python 3.9+ and Git.
50
50
 
51
+ ### Run the local IntHub shell
52
+
53
+ The first read-only IntHub shell now ships with the package:
54
+
55
+ ```bash
56
+ inthub-api --db-path .inthub/inthub.db
57
+ inthub-web --api-base-url http://127.0.0.1:8000
58
+ ```
59
+
60
+ If you are running from source instead of an installed package, the same entrypoints are available with:
61
+
62
+ ```bash
63
+ python -m apps.inthub_api --db-path .inthub/inthub.db
64
+ python -m apps.inthub_web --api-base-url http://127.0.0.1:8000
65
+ ```
66
+
67
+ Then use `itt hub login`, `itt hub link`, and `itt hub sync` from a local Intent workspace to populate the read-only IntHub project view.
68
+
51
69
  ### Install the skills.sh skill
52
70
 
53
71
  ```bash
@@ -149,11 +167,15 @@ All data lives in `.intent/` at your git repo root:
149
167
  decision-001.json
150
168
  ```
151
169
 
170
+ `.intent/` is local semantic-workspace metadata. It should stay out of Git history and should remain ignored by `.gitignore`.
171
+
152
172
  ## Docs
153
173
 
154
174
  - [Vision](docs/EN/vision.md) — why semantic history matters. **If this project interests you, start here.**
155
175
  - [CLI Design](docs/EN/cli.md) — object model, commands, JSON contract
156
176
  - [Roadmap](docs/EN/roadmap.md) — phase plan
177
+ - [IntHub MVP](docs/EN/inthub-mvp.md) — first remote collaboration-layer scope
178
+ - [IntHub Sync Contract](docs/EN/inthub-sync-contract.md) — first sync, identity, and API contract
157
179
 
158
180
  ## License
159
181
 
@@ -0,0 +1 @@
1
+ """App packages for the Intent monorepo."""
@@ -0,0 +1 @@
1
+ """IntHub API package."""
@@ -0,0 +1,4 @@
1
+ from apps.inthub_api.server import main
2
+
3
+
4
+ main()
@@ -0,0 +1,43 @@
1
+ """Shared helpers for the IntHub API."""
2
+
3
+ import uuid
4
+ from datetime import datetime, timezone
5
+
6
+
7
+ def now_utc():
8
+ return datetime.now(timezone.utc).isoformat()
9
+
10
+
11
+ def new_id(prefix):
12
+ return f"{prefix}_{uuid.uuid4().hex[:12]}"
13
+
14
+
15
+ def make_remote_object_id(workspace_id, local_object_id):
16
+ return f"{workspace_id}__{local_object_id}"
17
+
18
+
19
+ def split_remote_object_id(remote_object_id):
20
+ parts = remote_object_id.split("__", 1)
21
+ if len(parts) != 2:
22
+ raise ValueError("Invalid remote object ID.")
23
+ return parts[0], parts[1]
24
+
25
+
26
+ class APIError(Exception):
27
+ def __init__(self, code, message, status=400, details=None):
28
+ super().__init__(message)
29
+ self.code = code
30
+ self.message = message
31
+ self.status = status
32
+ self.details = details or {}
33
+
34
+
35
+ def require_repo(repo):
36
+ required = ("provider", "repo_id", "owner", "name")
37
+ missing = [key for key in required if not repo.get(key)]
38
+ if missing:
39
+ raise APIError(
40
+ "INVALID_INPUT",
41
+ f"Missing repo fields: {', '.join(missing)}.",
42
+ status=400,
43
+ )
@@ -0,0 +1,47 @@
1
+ """SQLite helpers for the IntHub API."""
2
+
3
+ import sqlite3
4
+ from pathlib import Path
5
+
6
+
7
+ def connect(db_path):
8
+ path = Path(db_path)
9
+ path.parent.mkdir(parents=True, exist_ok=True)
10
+ conn = sqlite3.connect(path, check_same_thread=False)
11
+ conn.row_factory = sqlite3.Row
12
+ init_db(conn)
13
+ return conn
14
+
15
+
16
+ def init_db(conn):
17
+ conn.executescript(
18
+ """
19
+ CREATE TABLE IF NOT EXISTS projects (
20
+ id TEXT PRIMARY KEY,
21
+ name TEXT NOT NULL,
22
+ provider TEXT NOT NULL,
23
+ repo_id TEXT NOT NULL UNIQUE,
24
+ owner TEXT NOT NULL,
25
+ repo_name TEXT NOT NULL,
26
+ created_at TEXT NOT NULL
27
+ );
28
+
29
+ CREATE TABLE IF NOT EXISTS workspaces (
30
+ id TEXT PRIMARY KEY,
31
+ project_id TEXT NOT NULL,
32
+ provider TEXT NOT NULL,
33
+ repo_id TEXT NOT NULL,
34
+ created_at TEXT NOT NULL
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS sync_batches (
38
+ id TEXT PRIMARY KEY,
39
+ project_id TEXT NOT NULL,
40
+ workspace_id TEXT NOT NULL,
41
+ generated_at TEXT NOT NULL,
42
+ accepted_at TEXT NOT NULL,
43
+ payload_json TEXT NOT NULL
44
+ );
45
+ """
46
+ )
47
+ conn.commit()
@@ -0,0 +1,170 @@
1
+ """Write-path logic for the IntHub API."""
2
+
3
+ import json
4
+
5
+ from apps.inthub_api.common import APIError, new_id, now_utc, require_repo
6
+ from apps.inthub_api.db import connect
7
+
8
+
9
+ def link_project(db_path, project_name, repo, workspace_id):
10
+ require_repo(repo)
11
+ if repo.get("provider") != "github":
12
+ raise APIError(
13
+ "PROVIDER_UNSUPPORTED",
14
+ f"Unsupported provider '{repo.get('provider')}'.",
15
+ status=400,
16
+ )
17
+
18
+ with connect(db_path) as conn:
19
+ project = conn.execute(
20
+ "SELECT * FROM projects WHERE provider = ? AND repo_id = ?",
21
+ (repo["provider"], repo["repo_id"]),
22
+ ).fetchone()
23
+
24
+ if project is None:
25
+ project_id = new_id("proj")
26
+ conn.execute(
27
+ """
28
+ INSERT INTO projects (id, name, provider, repo_id, owner, repo_name, created_at)
29
+ VALUES (?, ?, ?, ?, ?, ?, ?)
30
+ """,
31
+ (
32
+ project_id,
33
+ project_name or repo["name"],
34
+ repo["provider"],
35
+ repo["repo_id"],
36
+ repo["owner"],
37
+ repo["name"],
38
+ now_utc(),
39
+ ),
40
+ )
41
+ else:
42
+ project_id = project["id"]
43
+
44
+ if not workspace_id:
45
+ workspace_id = new_id("wks")
46
+
47
+ workspace = conn.execute(
48
+ "SELECT * FROM workspaces WHERE id = ?",
49
+ (workspace_id,),
50
+ ).fetchone()
51
+ if workspace is None:
52
+ conn.execute(
53
+ """
54
+ INSERT INTO workspaces (id, project_id, provider, repo_id, created_at)
55
+ VALUES (?, ?, ?, ?, ?)
56
+ """,
57
+ (workspace_id, project_id, repo["provider"], repo["repo_id"], now_utc()),
58
+ )
59
+ else:
60
+ if workspace["project_id"] != project_id or workspace["repo_id"] != repo["repo_id"]:
61
+ raise APIError(
62
+ "STATE_CONFLICT",
63
+ f"Workspace {workspace_id} is already linked to another project or repo.",
64
+ status=409,
65
+ )
66
+
67
+ conn.commit()
68
+ return {
69
+ "project_id": project_id,
70
+ "workspace_id": workspace_id,
71
+ "repo_binding": {
72
+ "provider": repo["provider"],
73
+ "repo_id": repo["repo_id"],
74
+ "owner": repo["owner"],
75
+ "name": repo["name"],
76
+ },
77
+ }
78
+
79
+
80
+ def store_sync_batch(db_path, payload):
81
+ required = ("sync_batch_id", "project_id", "repo", "workspace", "snapshot")
82
+ missing = [key for key in required if key not in payload]
83
+ if missing:
84
+ raise APIError(
85
+ "INVALID_INPUT",
86
+ f"Missing sync batch fields: {', '.join(missing)}.",
87
+ status=400,
88
+ )
89
+
90
+ repo = payload["repo"]
91
+ workspace = payload["workspace"]
92
+ require_repo(repo)
93
+
94
+ workspace_id = workspace.get("workspace_id")
95
+ if not workspace_id:
96
+ raise APIError("INVALID_INPUT", "Missing workspace.workspace_id.", status=400)
97
+
98
+ with connect(db_path) as conn:
99
+ existing = conn.execute(
100
+ "SELECT accepted_at FROM sync_batches WHERE id = ?",
101
+ (payload["sync_batch_id"],),
102
+ ).fetchone()
103
+ if existing is not None:
104
+ return {
105
+ "sync_batch_id": payload["sync_batch_id"],
106
+ "project_id": payload["project_id"],
107
+ "workspace_id": workspace_id,
108
+ "accepted_at": existing["accepted_at"],
109
+ "duplicate": True,
110
+ }
111
+
112
+ project = conn.execute(
113
+ "SELECT * FROM projects WHERE id = ?",
114
+ (payload["project_id"],),
115
+ ).fetchone()
116
+ if project is None:
117
+ raise APIError(
118
+ "OBJECT_NOT_FOUND",
119
+ f"Project {payload['project_id']} not found.",
120
+ status=404,
121
+ )
122
+ if project["provider"] != repo["provider"] or project["repo_id"] != repo["repo_id"]:
123
+ raise APIError(
124
+ "STATE_CONFLICT",
125
+ "Sync batch repo does not match the linked project repo.",
126
+ status=409,
127
+ )
128
+
129
+ workspace_row = conn.execute(
130
+ "SELECT * FROM workspaces WHERE id = ? AND project_id = ?",
131
+ (workspace_id, payload["project_id"]),
132
+ ).fetchone()
133
+ if workspace_row is None:
134
+ raise APIError(
135
+ "OBJECT_NOT_FOUND",
136
+ f"Workspace {workspace_id} is not linked to project {payload['project_id']}.",
137
+ status=404,
138
+ )
139
+
140
+ snapshot = payload["snapshot"]
141
+ if snapshot.get("schema_version") != "1.0":
142
+ raise APIError(
143
+ "SCHEMA_VERSION_MISMATCH",
144
+ f"Unsupported schema_version '{snapshot.get('schema_version')}'.",
145
+ status=400,
146
+ )
147
+
148
+ accepted_at = now_utc()
149
+ conn.execute(
150
+ """
151
+ INSERT INTO sync_batches (id, project_id, workspace_id, generated_at, accepted_at, payload_json)
152
+ VALUES (?, ?, ?, ?, ?, ?)
153
+ """,
154
+ (
155
+ payload["sync_batch_id"],
156
+ payload["project_id"],
157
+ workspace_id,
158
+ payload.get("generated_at", accepted_at),
159
+ accepted_at,
160
+ json.dumps(payload, ensure_ascii=False),
161
+ ),
162
+ )
163
+ conn.commit()
164
+ return {
165
+ "sync_batch_id": payload["sync_batch_id"],
166
+ "project_id": payload["project_id"],
167
+ "workspace_id": workspace_id,
168
+ "accepted_at": accepted_at,
169
+ "duplicate": False,
170
+ }