flowstatemcp 2.5.0__py3-none-any.whl

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 (143) hide show
  1. flowstate/__init__.py +34 -0
  2. flowstate/__main__.py +7 -0
  3. flowstate/activity.py +224 -0
  4. flowstate/agent_guide.py +251 -0
  5. flowstate/app_keys.py +141 -0
  6. flowstate/approvals.py +974 -0
  7. flowstate/approve_page.py +565 -0
  8. flowstate/ask.py +107 -0
  9. flowstate/autosession.py +480 -0
  10. flowstate/backup.py +201 -0
  11. flowstate/brief.py +420 -0
  12. flowstate/card_offer.py +208 -0
  13. flowstate/card_taps.py +165 -0
  14. flowstate/card_trust.py +127 -0
  15. flowstate/claims.py +461 -0
  16. flowstate/cleanup.py +619 -0
  17. flowstate/cli.py +728 -0
  18. flowstate/cli_data.py +434 -0
  19. flowstate/cli_more.py +302 -0
  20. flowstate/cli_review.py +459 -0
  21. flowstate/cloudflared_check.py +49 -0
  22. flowstate/conflict_card_ui.py +286 -0
  23. flowstate/conflict_cards.py +1193 -0
  24. flowstate/conflict_reopen.py +320 -0
  25. flowstate/conflicts.py +420 -0
  26. flowstate/connections.py +33 -0
  27. flowstate/database.py +2191 -0
  28. flowstate/dbconn.py +308 -0
  29. flowstate/deletes.py +860 -0
  30. flowstate/embeddings.py +354 -0
  31. flowstate/erase.py +1047 -0
  32. flowstate/extras.py +71 -0
  33. flowstate/git_capture.py +157 -0
  34. flowstate/guard.py +224 -0
  35. flowstate/handoffs.py +168 -0
  36. flowstate/history.py +755 -0
  37. flowstate/history_page.py +276 -0
  38. flowstate/http_app.py +204 -0
  39. flowstate/identity.py +387 -0
  40. flowstate/install_name.py +140 -0
  41. flowstate/ledger.py +272 -0
  42. flowstate/legacy_args.py +65 -0
  43. flowstate/links.py +609 -0
  44. flowstate/markdown_io.py +242 -0
  45. flowstate/meaning.py +398 -0
  46. flowstate/memory_import.py +92 -0
  47. flowstate/migrations/001_auto_sessions.sql +16 -0
  48. flowstate/migrations/002_review_queue.sql +33 -0
  49. flowstate/migrations/003_links.sql +47 -0
  50. flowstate/migrations/004_review_mode.sql +24 -0
  51. flowstate/migrations/005_history.sql +21 -0
  52. flowstate/migrations/006_activity.sql +26 -0
  53. flowstate/migrations/007_meaning_index.sql +16 -0
  54. flowstate/migrations/008_git_capture.sql +12 -0
  55. flowstate/migrations/009_hard_rules.sql +13 -0
  56. flowstate/migrations/010_handoffs.sql +14 -0
  57. flowstate/migrations/011_record_meta.sql +13 -0
  58. flowstate/migrations/012_accountability.sql +71 -0
  59. flowstate/migrations/013_todo_claims.sql +36 -0
  60. flowstate/migrations/014_decision_conflicts.sql +41 -0
  61. flowstate/migrations/015_session_runs.sql +17 -0
  62. flowstate/migrations/016_write_receipts.sql +35 -0
  63. flowstate/migrations/017_run_views.sql +26 -0
  64. flowstate/migrations/018_run_views_upto.sql +4 -0
  65. flowstate/migrations/019_approvals.sql +47 -0
  66. flowstate/migrations/020_source_stamps.sql +10 -0
  67. flowstate/migrations/021_review_links.sql +11 -0
  68. flowstate/migrations/022_review_start_tickets.sql +10 -0
  69. flowstate/migrations/023_conflict_cards.sql +68 -0
  70. flowstate/migrations/024_rule_confirmations.sql +7 -0
  71. flowstate/migrations/025_confirmed_hash.sql +7 -0
  72. flowstate/migrations/__init__.py +149 -0
  73. flowstate/models.py +307 -0
  74. flowstate/names.py +157 -0
  75. flowstate/offsite.py +386 -0
  76. flowstate/offsite_crypto.py +272 -0
  77. flowstate/paths.py +265 -0
  78. flowstate/popup.py +238 -0
  79. flowstate/priority.py +66 -0
  80. flowstate/privatefs.py +476 -0
  81. flowstate/project_history.py +710 -0
  82. flowstate/provenance.py +696 -0
  83. flowstate/receipts.py +377 -0
  84. flowstate/relay/__init__.py +72 -0
  85. flowstate/relay/acme.py +271 -0
  86. flowstate/relay/api.py +164 -0
  87. flowstate/relay/cli.py +344 -0
  88. flowstate/relay/crypto.py +137 -0
  89. flowstate/relay/ct.py +118 -0
  90. flowstate/relay/runner.py +177 -0
  91. flowstate/relay/store.py +191 -0
  92. flowstate/relay/tunnel.py +413 -0
  93. flowstate/release_signers +14 -0
  94. flowstate/remote_auth.py +1219 -0
  95. flowstate/review.py +320 -0
  96. flowstate/review_apply.py +140 -0
  97. flowstate/review_card_ui.py +241 -0
  98. flowstate/review_cards.py +564 -0
  99. flowstate/review_mode.py +274 -0
  100. flowstate/review_page.py +1361 -0
  101. flowstate/review_phone.py +465 -0
  102. flowstate/review_tunnel.py +338 -0
  103. flowstate/routing.py +392 -0
  104. flowstate/rules.py +418 -0
  105. flowstate/safety.py +98 -0
  106. flowstate/saves.py +83 -0
  107. flowstate/scheduler.py +160 -0
  108. flowstate/schema.sql +713 -0
  109. flowstate/search.py +355 -0
  110. flowstate/secret_store.py +271 -0
  111. flowstate/secrets_scan.py +135 -0
  112. flowstate/selfupdate.py +1020 -0
  113. flowstate/server.py +1299 -0
  114. flowstate/session_contents.py +382 -0
  115. flowstate/session_records.py +629 -0
  116. flowstate/site_style.py +46 -0
  117. flowstate/stored.py +158 -0
  118. flowstate/story.py +442 -0
  119. flowstate/surface.py +173 -0
  120. flowstate/tasks.py +231 -0
  121. flowstate/term.py +40 -0
  122. flowstate/tool_labels.py +104 -0
  123. flowstate/tooldefs.py +383 -0
  124. flowstate/tools/__init__.py +118 -0
  125. flowstate/tools/core.py +545 -0
  126. flowstate/tools/files.py +416 -0
  127. flowstate/tools/git_ops.py +1081 -0
  128. flowstate/tools/intelligence.py +376 -0
  129. flowstate/tools/learning.py +282 -0
  130. flowstate/tools/problems.py +159 -0
  131. flowstate/tools/sessions.py +266 -0
  132. flowstate/tools/story.py +48 -0
  133. flowstate/tools/todos.py +79 -0
  134. flowstate/tools/utils.py +71 -0
  135. flowstate/tools/variables.py +345 -0
  136. flowstate/updates.py +482 -0
  137. flowstate/urls.py +7 -0
  138. flowstate/waiting.py +828 -0
  139. flowstate/who.py +302 -0
  140. flowstatemcp-2.5.0.dist-info/METADATA +88 -0
  141. flowstatemcp-2.5.0.dist-info/RECORD +143 -0
  142. flowstatemcp-2.5.0.dist-info/WHEEL +4 -0
  143. flowstatemcp-2.5.0.dist-info/entry_points.txt +3 -0
flowstate/who.py ADDED
@@ -0,0 +1,302 @@
1
+ """Who did what: one list of recent recorded changes across FlowState's logs (v2, after
2
+ migration 012), newest first, with the app, model, machine and identity source of each.
3
+
4
+ Sources: edit history (edits and undos), the review queue (queued items, approvals,
5
+ rejections), links (added / removed), handoff notes (left / picked up), hard rules (added /
6
+ removed), review-mode changes, records created by a write (activity log), and todo claims
7
+ (claim, renew, release, handoff, override, cleared; migration 013), and possible conflicting
8
+ decisions (flagged, resolved by a replacing decision, dismissed as compatible; 014). Rows written
9
+ before migration 012 show the app name the older columns kept and "not recorded" for the
10
+ rest.
11
+
12
+ Used by the review page ("Who did what") and, read-only, by history(action="who").
13
+ Filters: app, model, machine (each: contains, any case), project id, since / until (local
14
+ time of this computer: "YYYY-MM-DD" or "YYYY-MM-DD HH:MM").
15
+ """
16
+
17
+ import json
18
+ import sqlite3
19
+ from datetime import datetime, timedelta, timezone
20
+ from pathlib import Path
21
+ from typing import Optional
22
+
23
+ from . import dbconn
24
+ from . import history, identity
25
+
26
+ PER_PAGE = 50
27
+ MAX_PER_PAGE = 100
28
+ _ID = ("app", "model", "machine", "identity_source")
29
+ _VIA_IN = ", ".join(f"'{x}'" for x in identity.VIA_SOURCES)
30
+ _SIGNED_IN = "instr(COALESCE(u.machine, ''), '(signed in as ')"
31
+
32
+
33
+ class WhoError(Exception):
34
+ def __init__(self, code: str, message: str):
35
+ super().__init__(message)
36
+ self.payload = {"error": code, "message": message}
37
+
38
+
39
+ def _tables(conn) -> dict:
40
+ names = [r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")]
41
+ return {n: {r[1] for r in conn.execute(f'PRAGMA table_info("{n}")')} for n in names}
42
+
43
+
44
+ def _id_cols(cols: set, prefix: str = "", fallback_app: str = "NULL") -> str:
45
+ """app, model, machine, identity_source expressions for one source (NULL before 012)."""
46
+ out = []
47
+ for f in _ID:
48
+ c = prefix + f
49
+ expr = c if c in cols else "NULL"
50
+ if f == "app":
51
+ expr = f"COALESCE({expr}, {fallback_app})" if expr != "NULL" else fallback_app
52
+ out.append(f"{expr} AS {f}")
53
+ return ", ".join(out)
54
+
55
+
56
+ def _sel(at: str, ids: str, project: str, rtype: str, rid: str, what: str, kind: str,
57
+ extra: str, rest: str) -> str:
58
+ return (f"SELECT {at} AS at, {ids}, {project} AS project_id, {rtype} AS record_type, "
59
+ f"{rid} AS record_id, {what} AS what, '{kind}' AS kind, id AS ref, "
60
+ f"{extra} AS extra {rest}")
61
+
62
+
63
+ def _sources(t: dict) -> list[str]:
64
+ """One SELECT per kind of event, all with the same columns:
65
+ at, app, model, machine, identity_source, project_id, record_type, record_id, what,
66
+ kind, ref, extra."""
67
+ q = []
68
+ if "record_history" in t:
69
+ c = t["record_history"]
70
+ q.append(_sel("created_at", _id_cols(c, fallback_app="client"), "project_id",
71
+ "table_name", "record_id", "tool", "edit", "undone_at",
72
+ # a creation is listed once, from the activity log (and the review queue)
73
+ "FROM record_history WHERE NOT (old_row IS NULL AND (tool LIKE 'create/%' "
74
+ "OR tool LIKE 'approve/%'))"))
75
+ if "review_items" in t:
76
+ c = t["review_items"]
77
+ q.append(_sel("created_at", _id_cols(c, fallback_app="source"), "project_id",
78
+ "'review_item'", "id",
79
+ "'queued ' || kind || ' for review: ' || substr(summary, 1, 60)",
80
+ "queued", "NULL", "FROM review_items"))
81
+ q.append(_sel("decided_at", _id_cols(c, "decided_", "decided_by"), "project_id",
82
+ "'review_item'", "id",
83
+ "status || ' review item #' || id || CASE WHEN result_table IS NOT NULL "
84
+ "AND result_id IS NOT NULL THEN ' (became ' || result_table || ' #' "
85
+ "|| result_id || ')' ELSE '' END",
86
+ "decided", "NULL", "FROM review_items WHERE decided_at IS NOT NULL"))
87
+ if "record_links" in t:
88
+ c = t["record_links"]
89
+ target = "COALESCE(url, to_type || ' #' || to_id)"
90
+ q.append(_sel("created_at", _id_cols(c, fallback_app="created_by"), "from_project_id",
91
+ "'link'", "id",
92
+ f"'linked ' || from_type || ' #' || from_id || ' ' || link_type || ' ' "
93
+ f"|| {target}", "link_added", "NULL", "FROM record_links"))
94
+ q.append(_sel("removed_at", _id_cols(c, "removed_", "removed_by"), "from_project_id",
95
+ "'link'", "id",
96
+ f"'removed link #' || id || ' (' || link_type || ' ' || {target} || ')'",
97
+ "link_removed", "NULL", "FROM record_links WHERE removed_at IS NOT NULL"))
98
+ if "handoff_notes" in t:
99
+ c = t["handoff_notes"]
100
+ q.append(_sel("created_at", _id_cols(c, fallback_app="created_by"), "project_id",
101
+ "'handoff'", "id", "'left handoff: ' || substr(where_i_was, 1, 60)",
102
+ "handoff_left", "NULL", "FROM handoff_notes"))
103
+ q.append(_sel("picked_up_at", _id_cols(c, "picked_up_", "picked_up_by"), "project_id",
104
+ "'handoff'", "id", "'picked up handoff H' || id", "handoff_picked_up",
105
+ "NULL", "FROM handoff_notes WHERE picked_up_at IS NOT NULL"))
106
+ if "project_rules" in t:
107
+ c = t["project_rules"]
108
+ q.append(_sel("created_at", _id_cols(c, fallback_app="created_by"), "project_id",
109
+ "'rule'", "id", "'added hard rule: ' || substr(rule, 1, 60)",
110
+ "rule_added", "NULL", "FROM project_rules"))
111
+ q.append(_sel("removed_at", _id_cols(c, "removed_", "removed_by"), "project_id",
112
+ "'rule'", "id", "'removed hard rule R' || id", "rule_removed", "NULL",
113
+ "FROM project_rules WHERE removed_at IS NOT NULL"))
114
+ if "review_mode_log" in t:
115
+ c = t["review_mode_log"]
116
+ q.append(_sel("changed_at", _id_cols(c, fallback_app="client"),
117
+ "CASE WHEN scope LIKE 'project:%' THEN CAST(substr(scope, 9) AS INTEGER) END",
118
+ "'review_mode'", "id",
119
+ "'review mode ' || COALESCE(new_mode, 'override cleared') || ' (' || scope "
120
+ "|| ')'", "mode", "NULL", "FROM review_mode_log"))
121
+ if "activity_log" in t and "record_type" in t["activity_log"]:
122
+ c = t["activity_log"]
123
+ q.append(_sel("at", _id_cols(c, fallback_app="app"), "project_id", "record_type",
124
+ "record_id",
125
+ "'created ' || record_type || ' #' || record_id || ' (' || tool "
126
+ "|| COALESCE('/' || action, '') || ')'", "created", "NULL",
127
+ "FROM activity_log WHERE record_type IS NOT NULL AND ok = 1"))
128
+ if "todo_claim_events" in t:
129
+ c = t["todo_claim_events"]
130
+ q.append(f"SELECT at, {_id_cols(c)}, project_id, 'todo' AS record_type, todo_id AS "
131
+ f"record_id, summary AS what, 'todo_' || event AS kind, id AS ref, NULL AS "
132
+ f"extra FROM todo_claim_events")
133
+ if "decision_conflicts" in t:
134
+ c = t["decision_conflicts"]
135
+ pair = "'D' || new_id || ' vs D' || old_id"
136
+ q.append(_sel("created_at", _id_cols(c), "project_id", "'decision'", "new_id",
137
+ f"'possible conflict flagged: ' || {pair} || ' (score ' || "
138
+ f"printf('%.2f', score) || ')'", "conflict_flagged", "NULL",
139
+ "FROM decision_conflicts"))
140
+ q.append(_sel("resolved_at", _id_cols(c, "resolved_"), "project_id", "'decision'",
141
+ "new_id",
142
+ f"'possible conflict ' || {pair} || CASE status WHEN 'resolved_replaced' "
143
+ f"THEN ' resolved: D' || resolved_by_decision || ' replaced one' "
144
+ f"ELSE ' dismissed: the user said they are compatible' END",
145
+ "conflict_resolved", "NULL",
146
+ "FROM decision_conflicts WHERE resolved_at IS NOT NULL"))
147
+ return q
148
+
149
+
150
+ def parse_time(value, end: bool = False) -> Optional[str]:
151
+ """Local "YYYY-MM-DD[ HH:MM[:SS]]" -> UTC "YYYY-MM-DD HH:MM:SS" (how FlowState stores
152
+ times). A bare date as `end` means the end of that day."""
153
+ if value in (None, ""):
154
+ return None
155
+ text = str(value).strip().replace("T", " ")
156
+ for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d"):
157
+ try:
158
+ dt = datetime.strptime(text, fmt)
159
+ except ValueError:
160
+ continue
161
+ if fmt == "%Y-%m-%d" and end:
162
+ dt += timedelta(days=1, seconds=-1)
163
+ return dt.astimezone().astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
164
+ raise WhoError("bad_time", f"Times look like 2026-10-17 or 2026-10-17 14:30 (local time), "
165
+ f"not {value!r}.")
166
+
167
+
168
+ def local_time(utc: Optional[str]) -> str:
169
+ if not utc:
170
+ return ""
171
+ try:
172
+ dt = datetime.strptime(utc[:19], "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
173
+ except ValueError:
174
+ return utc
175
+ return dt.astimezone().strftime("%Y-%m-%d %H:%M:%S")
176
+
177
+
178
+ def _undoable(conn, row: dict) -> bool:
179
+ """The edit history's own rule: the last not-undone edit of a record can be undone."""
180
+ if row["kind"] != "edit" or row["extra"] or row["project_id"] is None \
181
+ or row["record_type"] not in history.TYPE_OF:
182
+ return False
183
+ last = conn.execute("SELECT MAX(id) FROM record_history WHERE table_name = ? AND "
184
+ "record_id = ? AND undone_at IS NULL",
185
+ (row["record_type"], row["record_id"])).fetchone()[0]
186
+ return last == row["ref"]
187
+
188
+
189
+ def _edit_what(conn, row: dict) -> str:
190
+ h = conn.execute("SELECT old_row, new_row, undo_of FROM record_history WHERE id = ?",
191
+ (row["ref"],)).fetchone()
192
+ if h is None:
193
+ return row["what"] or ""
194
+ old = json.loads(h["old_row"]) if h["old_row"] else None
195
+ new = json.loads(h["new_row"]) if h["new_row"] else None
196
+ ch = history._changed_fields(old, new)
197
+ what = "created" if ch.get("created") else "deleted" if ch.get("deleted") else \
198
+ ", ".join(sorted(ch)) or "no field changes"
199
+ out = f"{row['what']}: {what}"
200
+ if row["extra"]:
201
+ out += " (undone)"
202
+ return out[:120]
203
+
204
+
205
+ _TYPE_OF_EVENT = {"link_added": "link", "link_removed": "link", "handoff_left": "handoff",
206
+ "handoff_picked_up": "handoff", "rule_added": "rule", "rule_removed": "rule",
207
+ "queued": "review_item", "decided": "review_item", "mode": "review_mode_change"}
208
+
209
+
210
+ def _session_of(conn, row: dict, rtype: Optional[str], has: dict) -> Optional[int]:
211
+ """The automatic session (sessions.id) behind a row, from the 2.4 run ledger, so two
212
+ windows of one app are told apart. None when it wasn't recorded (before 2.4)."""
213
+ if "run_writes" not in has:
214
+ return None
215
+ if row["kind"] == "edit":
216
+ r = conn.execute("SELECT w.session_id FROM record_history h JOIN run_writes w "
217
+ "ON w.change_id = h.edit_id WHERE h.id = ?", (row["ref"],)).fetchone()
218
+ return r[0] if r else None
219
+ if row["kind"] == "queued" and "session_id" in has.get("review_items", ()):
220
+ r = conn.execute("SELECT session_id FROM review_items WHERE id = ?", (row["ref"],)).fetchone()
221
+ if r and r[0]:
222
+ return r[0]
223
+ if row["kind"].startswith("todo_") and "session_runs" in has:
224
+ r = conn.execute(
225
+ "SELECT r.session_id FROM todo_claim_events e JOIN session_runs r ON r.conn_key = e.session "
226
+ "JOIN auto_sessions a ON a.session_id = r.session_id AND a.project_id IS e.project_id "
227
+ "WHERE e.id = ? ORDER BY r.session_id DESC LIMIT 1", (row["ref"],)).fetchone()
228
+ return r[0] if r else None
229
+ kind = _TYPE_OF_EVENT.get(row["kind"], rtype)
230
+ if not kind or row["record_id"] is None:
231
+ return None
232
+ r = conn.execute("SELECT session_id FROM run_writes WHERE status = 'saved' AND records LIKE ? "
233
+ "ORDER BY abs(julianday(finished_at) - julianday(?)) LIMIT 1",
234
+ (f'%"{kind}:{row["record_id"]}"%', row["at"])).fetchone()
235
+ return r[0] if r else None
236
+
237
+
238
+ def recent(db_path: Path, app: Optional[str] = None, model: Optional[str] = None,
239
+ machine: Optional[str] = None, project_id: Optional[int] = None,
240
+ since=None, until=None, page: int = 1, per_page: int = PER_PAGE) -> dict:
241
+ """{"rows": [...], "total": n, "page": p, "pages": n, "per_page": k}."""
242
+ page = max(1, int(page or 1))
243
+ per_page = max(1, min(int(per_page or PER_PAGE), MAX_PER_PAGE))
244
+ since_u, until_u = parse_time(since), parse_time(until, end=True)
245
+ conn = dbconn.connect(str(db_path), timeout=5.0)
246
+ conn.row_factory = sqlite3.Row
247
+ try:
248
+ has = _tables(conn)
249
+ parts = _sources(has)
250
+ if not parts:
251
+ return {"rows": [], "total": 0, "page": 1, "pages": 1, "per_page": per_page}
252
+ where, params = ["u.at IS NOT NULL"], []
253
+ for col, val in (("app", app), ("model", model), ("machine", machine)):
254
+ if val in (None, ""):
255
+ continue
256
+ test = f"instr(lower(COALESCE(u.{col}, '')), lower(?)) > 0"
257
+ params.append(str(val).strip())
258
+ if col == "app": # also the key / token the app signed in with (identity.via)
259
+ test = f"({test} OR (u.identity_source IN ({_VIA_IN}) AND {_SIGNED_IN} > 0 " \
260
+ f"AND instr(lower(substr(u.machine, {_SIGNED_IN})), lower(?)) > 0))"
261
+ params.append(str(val).strip())
262
+ where.append(test)
263
+ if project_id is not None:
264
+ where.append("u.project_id = ?")
265
+ params.append(int(project_id))
266
+ if since_u:
267
+ where.append("u.at >= ?")
268
+ params.append(since_u)
269
+ if until_u:
270
+ where.append("u.at <= ?")
271
+ params.append(until_u)
272
+ union = " UNION ALL ".join(parts)
273
+ cond = " AND ".join(where)
274
+ total = conn.execute(f"SELECT COUNT(*) FROM ({union}) u WHERE {cond}", params).fetchone()[0]
275
+ rows = [dict(r) for r in conn.execute(
276
+ f"""SELECT u.*, p.name AS project FROM ({union}) u
277
+ LEFT JOIN projects p ON p.id = u.project_id WHERE {cond}
278
+ ORDER BY u.at DESC, u.kind, u.ref DESC LIMIT ? OFFSET ?""",
279
+ (*params, per_page, (page - 1) * per_page))]
280
+ out = []
281
+ for r in rows:
282
+ undo = _undoable(conn, r)
283
+ what = _edit_what(conn, r) if r["kind"] == "edit" else (r["what"] or "")
284
+ rtype = history.type_of(r["record_type"]) if r["kind"] == "edit" else r["record_type"]
285
+ out.append({"at": local_time(r["at"]), "at_utc": r["at"], "app": r["app"],
286
+ "model": r["model"], "machine": r["machine"],
287
+ "identity_source": r["identity_source"],
288
+ "identity": identity.label(r["identity_source"]),
289
+ # the relay / own-tunnel sign-in the app came in on (its name is the
290
+ # app's own claim there)
291
+ **({"via": v} if (v := identity.via(r["machine"], r["identity_source"]))
292
+ else {}),
293
+ "session": _session_of(conn, r, rtype, has),
294
+ "project_id": r["project_id"], "project": r["project"],
295
+ "record_type": rtype, "record_id": r["record_id"],
296
+ "what": " ".join(what.split())[:120], "event": r["kind"],
297
+ "undo": {"record_type": rtype, "record_id": r["record_id"],
298
+ "project_id": r["project_id"]} if undo else None})
299
+ return {"rows": out, "total": total, "page": page, "per_page": per_page,
300
+ "pages": max(1, -(-total // per_page))}
301
+ finally:
302
+ conn.close()
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.5
2
+ Name: flowstatemcp
3
+ Version: 2.5.0
4
+ Summary: Local-first shared project memory for AI apps (MCP server)
5
+ Project-URL: Homepage, https://flowstatemcp.com
6
+ Project-URL: Repository, https://github.com/dialectforge/FlowState
7
+ Project-URL: Changelog, https://github.com/dialectforge/FlowState/blob/main/CHANGELOG.md
8
+ Project-URL: Hosted, https://flowstatemcp.com/hosted
9
+ Author: John Martin
10
+ License-Expression: MIT
11
+ Keywords: ai,flowstatemcp,local-first,mcp,memory,model-context-protocol,sqlite
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: mcp<2,>=1.30
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: httpx2; extra == 'dev'
27
+ Requires-Dist: httpx>=0.27; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
30
+ Provides-Extra: embeddings
31
+ Requires-Dist: sentence-transformers>=2.2.0; extra == 'embeddings'
32
+ Requires-Dist: sqlite-vec>=0.1.0; extra == 'embeddings'
33
+ Provides-Extra: offsite
34
+ Requires-Dist: cryptography>=42; extra == 'offsite'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # flowstatemcp
38
+
39
+ Local-first shared project memory for AI apps. One SQLite file on your computer holds the
40
+ todos, changes, decisions, problems, and lessons your AI apps share. The import package is
41
+ `flowstate`. The PyPI name is `flowstatemcp`, because `flowstate` is a different project.
42
+
43
+ - Site: https://flowstatemcp.com
44
+ - Hosted (no install): https://flowstatemcp.com/hosted
45
+ - Source: https://github.com/dialectforge/FlowState
46
+
47
+ ## Install
48
+
49
+ You need Python 3.11+ and [uv](https://docs.astral.sh/uv/getting-started/installation/) for the
50
+ one-command form.
51
+
52
+ ```bash
53
+ uvx flowstatemcp
54
+ ```
55
+
56
+ That starts the stdio MCP server. These do the same thing:
57
+
58
+ ```bash
59
+ pipx run flowstatemcp
60
+ pip install flowstatemcp
61
+ flowstatemcp
62
+ flowstate
63
+ ```
64
+
65
+ `pip install flowstate` does not install this project.
66
+
67
+ The git clone is the fallback when you want `setup_flowstate.py` to write app configs, schedule
68
+ backups, and install the FlowState skill. See the repository
69
+ [README](https://github.com/dialectforge/FlowState/blob/main/README.md).
70
+
71
+ ## Run
72
+
73
+ ```bash
74
+ flowstatemcp # stdio (what desktop apps start)
75
+ flowstate # the same server
76
+ python -m flowstate --transport http --host 127.0.0.1 --port 8766
77
+ flowstate --help
78
+ ```
79
+
80
+ The database is `FLOWSTATE_DB` if set, otherwise the per-OS default in the repository AGENTS.md.
81
+
82
+ A plain `pip install flowstatemcp` (or of a built wheel) includes the base schema
83
+ (`flowstate/schema.sql`), the migrations, and `release_signers`. `flowstate update` only works
84
+ in a git clone set up by `setup_flowstate.py`. Like every FlowState 2.4.0 or later, it checks
85
+ GitHub once a day for a newer release (nothing about you or your projects is sent);
86
+ `flowstate update check off` or `FLOWSTATE_UPDATE_CHECK=0` turns that off.
87
+
88
+ Search by meaning is optional; see "Honest limits" in the repository README.
@@ -0,0 +1,143 @@
1
+ flowstate/__init__.py,sha256=aQCnanQZE_DnsFZjYUAScEExTYv8XNcR2avvXA0j924,829
2
+ flowstate/__main__.py,sha256=2RlXGEMr0aGU0JwKgyK19D08qnEWTeiqBPCAvcgOb9w,138
3
+ flowstate/activity.py,sha256=-4wht5c3LhGQff9cr0yvEsS31uQl3Wd9VGGiThtibAM,9944
4
+ flowstate/agent_guide.py,sha256=TA-QFlmovbC5U7xpkP8dnQsQBfYaFMFxQm5FZSF1bD4,16145
5
+ flowstate/app_keys.py,sha256=fduGUyoGDzLGqVhqvDtEvj6HIXrst3aYpN3c1h-Lu20,6680
6
+ flowstate/approvals.py,sha256=kCu1HSQ1vgKQdeB97EwqrSMLX1ImWY2h4m-vh8wUJyo,46646
7
+ flowstate/approve_page.py,sha256=deOdd2r8x9OiBlJgTL-PQLqS3DKHlKcigmcJHyDOOAE,29379
8
+ flowstate/ask.py,sha256=1--5VyUssbfIqplJOCX_zhCIF37_LjONuzdxVC6aOKE,5226
9
+ flowstate/autosession.py,sha256=XQVHeHrmLx7-uI56ev04oqCu5fzD1USMSd3vmjCfwhU,23180
10
+ flowstate/backup.py,sha256=2rhxbj5r3y2yd84v-qAmAT9-T4kopkyuUUsPQRoDUz8,7967
11
+ flowstate/brief.py,sha256=ZPgm7B_dNRgUZQkndMuAmKhDLrRMlK_aStJ-sLGbhg4,20529
12
+ flowstate/card_offer.py,sha256=QidtJY7UMeuA2Kr7cBoziyjgmIdhxESr2WUo6ot24NU,9854
13
+ flowstate/card_taps.py,sha256=6pFMLpNdGiBxrIBUOSEZfD8GuM1u5GjNL3eX9kPSX_w,6505
14
+ flowstate/card_trust.py,sha256=lv91lC5ItjhgOsPQeWHTHriEEp3iCF-e8mXTR-cn5wE,5873
15
+ flowstate/claims.py,sha256=5lGA75TUF0DyhvIc2K1Ktc4VrYkrfALjluaOWFtX-zI,21909
16
+ flowstate/cleanup.py,sha256=HjHaNE7u_1JytkXkdOt_uqAjrIXGlsmjHNlmZNY0rEM,31251
17
+ flowstate/cli.py,sha256=eEdu_bn5NTYjX6SdgdeBAUE9UJcH1FRHT9wYeAIs4oA,33122
18
+ flowstate/cli_data.py,sha256=ZY9MlUkYZSi3kUvig3s_Jtyxt9hQKwssorW4gr3b2qI,19169
19
+ flowstate/cli_more.py,sha256=AEM1-p_jjSkE8gcvej6z6I_BVCxTzJJgY648lpRbjiI,13640
20
+ flowstate/cli_review.py,sha256=TPAWHgcdL0Dv0FRQw1NjK302EOqCKAZ6PKJDj6AXpO0,22069
21
+ flowstate/cloudflared_check.py,sha256=54TxR-g6fSPf1gwJvKqeBDDgXa2xyPgCWKpSAYE2Q2o,2085
22
+ flowstate/conflict_card_ui.py,sha256=5TaSZ57Eul7Dgp_Z9MAprcO39FRrToUScWaySSmGQh0,12936
23
+ flowstate/conflict_cards.py,sha256=S7Vv7DObfe1LQK4-63O-BMZ3_sq9FnpgfkdTTKcjdVQ,56897
24
+ flowstate/conflict_reopen.py,sha256=Pfq6nexeWSHWTcNH_yO76m5VagUQDgHAo5-sibs71zI,17114
25
+ flowstate/conflicts.py,sha256=Dl6Gw6vouA6Viyb6TWDfYN4N8jHTfX5dh_2uwBzo2p8,20875
26
+ flowstate/connections.py,sha256=ot-_RdFaDVDWadR7mLjrlI593gShFFK0cBPOjLXjdOM,1424
27
+ flowstate/database.py,sha256=uXYw89wFhVCMCDhCShhn3-f9llrYPnUHbxm6i1Eu93M,83413
28
+ flowstate/dbconn.py,sha256=UiQWPip93ZevMJ_sNs0ZWQLYMYmdG1PLu2ul4-O51Uk,10936
29
+ flowstate/deletes.py,sha256=J8Y-YhVvMZ3Wp1kSAw92X4udZjr_9UFCHDwgtNZP7Ps,45116
30
+ flowstate/embeddings.py,sha256=DRBplRHI9sQPtVESWtp90bgbJxj36ma0xYh18awLN-o,11304
31
+ flowstate/erase.py,sha256=S0hLtjly6l6KM7yyHrAnh9SpHXsUWPBVomb8NEJ3Clg,54987
32
+ flowstate/extras.py,sha256=um5-tomr3tzPYQxGFDNa6xp2AoWnvhwt1OiA4a7K_-I,3693
33
+ flowstate/git_capture.py,sha256=c6DzO94c1RnlGWkf1ctfN43CQX7N3mXcpKSaYO6_io0,6151
34
+ flowstate/guard.py,sha256=i-hlDi4KqNBDzSFkbF3cHvCeNfWOOaHRVHYaIbO0KVU,9721
35
+ flowstate/handoffs.py,sha256=SN8AHwIKKwkmhQIZdZaf7VQ2Sy-naSJ2ZMjfM8yyk80,7644
36
+ flowstate/history.py,sha256=quTlCo8m5gQFREJxjB3YZBQioBqL4QxjLVf-ncuVzFM,37688
37
+ flowstate/history_page.py,sha256=MLoUr4BSaQGx0wJDvXXnbEI0MYNnSm9P3JWvjl1rw_4,15957
38
+ flowstate/http_app.py,sha256=NRXvCFCnvgjd7rXN7W3_J7sWhAFQJL4y7fccVqyDn-A,11142
39
+ flowstate/identity.py,sha256=af7bUvUbJ8hIjGk-sTIKVcHFFUvXqEFh7IcQohUEBVQ,18656
40
+ flowstate/install_name.py,sha256=XPmZtvl6eBqeORGT-irL4B242sc5IcjtCezP7Axfx54,6067
41
+ flowstate/ledger.py,sha256=jxMRPKN4XEOesDXXeOGFxUU9xcpVEK-nYuC9t9iP_r0,12304
42
+ flowstate/legacy_args.py,sha256=7LNYvMGtY4SB7sUCNK3nkru0WNH6aqVjBCzc9gWgBsU,2950
43
+ flowstate/links.py,sha256=uLDkO3BuBcvJEyhUCQV4e7ED0fh5wdE2pWDlFIfcXf4,28908
44
+ flowstate/markdown_io.py,sha256=4hoPgsFa4A_MiP6TNqmf8go6yAy6OyIhf1EZ8M0JGzg,12028
45
+ flowstate/meaning.py,sha256=1r3abM9MfL3bmfus-6uAFzo_MWWBGrdAcaj0E15Fj1Y,17313
46
+ flowstate/memory_import.py,sha256=dBNMq6vISnv0QLOTKXECYJd-jjU9AFrJI3mIl6Zoazc,3452
47
+ flowstate/models.py,sha256=_5siwfuw_Tc2qTYs2OuOTyvszfZLXdjIporPTQlIMTQ,10928
48
+ flowstate/names.py,sha256=o18363e19WGhN7VPAmRYB8FgMADtSc64ZydIBg2NYU0,8162
49
+ flowstate/offsite.py,sha256=5A1SB_BLqc9DMQh2jYZGGH7Y5W3hWqEo8wqftCednyY,17902
50
+ flowstate/offsite_crypto.py,sha256=PAHsGXAM-nxc-3oOoG07X20UMz-T7dfQlh-XVY-278g,12127
51
+ flowstate/paths.py,sha256=yBc2nY8H7HHuJdKJIED4vrcVpfqx8a47oNhVmbj9Fec,10093
52
+ flowstate/popup.py,sha256=vHnUDloDjH3caxeoHlCfJ8hNj19oS6XQMy-Ws-PaGM8,11373
53
+ flowstate/priority.py,sha256=WtCH8y43esvhp9wEJNRRIFVUh_79C_5HspFlsNH53kY,2654
54
+ flowstate/privatefs.py,sha256=0dX21cSCkOhj5j-385ELUfdCi-Rwi66asSIqxg-i0SI,20701
55
+ flowstate/project_history.py,sha256=OpL64Pw6KXFAU5eUnQGgzoO7rxaGh6qNZOXaTKThXTM,40092
56
+ flowstate/provenance.py,sha256=4qwOR7MfmIV574P8ZYaEAv5Dze3WBhtMoLQaANrwoeg,32167
57
+ flowstate/receipts.py,sha256=Kle_mBX_dIglXK6ttXKB1-HNoW2ykFsoep8B9e8dA4s,17200
58
+ flowstate/release_signers,sha256=CXagqqy01-k3SzP42sbQ2lPdTdzOehnXtB117xqbWag,839
59
+ flowstate/remote_auth.py,sha256=RA8Yl_Rj8BMZSpAruaWaxkqaLqY0RoLDbKs3gsAfTb4,65072
60
+ flowstate/review.py,sha256=6YG9PaFSkBfEMkjCWSvtWNpsvP7RRsDj061L8sU_zek,15017
61
+ flowstate/review_apply.py,sha256=XCwPhzK_qxybOW5hdQZNOVYTIAUpdm6ZkrGrRe1st_o,8053
62
+ flowstate/review_card_ui.py,sha256=WjseaOecFXq7vXNmuHiLoYxlZkLJdzb34esk0o1PT-w,11934
63
+ flowstate/review_cards.py,sha256=IjVl5Y1JdQMEcbUBw3luxMaelPCiGIhu0KrXqFP_Ciw,30509
64
+ flowstate/review_mode.py,sha256=LyieLtGrGTRXW7B3CuMYupKmjztOCko-d91vjW91Sss,11609
65
+ flowstate/review_page.py,sha256=cqGhhmhIqadyw1JUJAzDqlw1CsbH6tXl9XePSHWOLNY,85713
66
+ flowstate/review_phone.py,sha256=rqyPZ4NE4utx0M38GzDq0M07MUev5pOLVO4KlU1-Wn8,24159
67
+ flowstate/review_tunnel.py,sha256=LN3Huxk2bcHA6sRL7hDoomlqQZoCG_TO_QocJPx1mhU,15933
68
+ flowstate/routing.py,sha256=meX1tneapssf1uow7TfJrk9lRpeGvdvpHQivLq09CA4,14868
69
+ flowstate/rules.py,sha256=Nlf9sWMUgoQT-3nkfRc1cARpq54P6gfois8WewGXiTk,19964
70
+ flowstate/safety.py,sha256=zMsV0YK29thmunxUUsy3cgKlEnk-yhctxKAhCqQwkCM,4027
71
+ flowstate/saves.py,sha256=PsohfKICVYpCuRrmuqYR10Uo_v_NJNkylCu6_eTpCSE,3776
72
+ flowstate/scheduler.py,sha256=HX1YGj6QAPTKQZW1_Po349Fb7MKoDTvRcumTttHR97s,7272
73
+ flowstate/schema.sql,sha256=OePIjl8jpsmt_YQMU7tBYFkhCVxyW59tuw4oUXvVGEY,31330
74
+ flowstate/search.py,sha256=tf0aIwQrxSAii1P_Y8yQ3gP-JNBSxKUzAFsCPUyzt74,11495
75
+ flowstate/secret_store.py,sha256=1cVuLhHUXWHNVbeSZz4rU9X5vL3U5Gy9fJxpqeyXEmg,11798
76
+ flowstate/secrets_scan.py,sha256=xC3VXVy_QTdklibZLjMUVbDkYdkXxM4ym1mKs0z5png,5749
77
+ flowstate/selfupdate.py,sha256=KkOEfvP7LCKyXRlPcs7l1fcuOorRRWrAtXKDLPY3gWs,52546
78
+ flowstate/server.py,sha256=Ub7Seiz3GxivpKePAoGXisxL9N3a1bDxFjjq-iA1uw4,63883
79
+ flowstate/session_contents.py,sha256=0w66zI1fy8lKzTdBF6lpDvHUmJlV2RIcQrf_ZUIftXA,18404
80
+ flowstate/session_records.py,sha256=KwZjJ3yBAkXlpUkKHoOCC2MlClwjWCvrCYRoAESJltc,31410
81
+ flowstate/site_style.py,sha256=TxOi1PmychYxbUyfGAanh9n29idUIID_gKRgls_hlHc,2681
82
+ flowstate/stored.py,sha256=W8cg_p6XveN6kFIrf29qx2pu9FveXRev-msDCR0K2pA,7239
83
+ flowstate/story.py,sha256=AXb45TKbI248A531Ioe_X0LTXJctytZ5iDQN9G5mtvE,16194
84
+ flowstate/surface.py,sha256=QK6MGKyftjdGVFKiPfgaWC86lHWGPaW9TDBao0i49rY,8939
85
+ flowstate/tasks.py,sha256=CGCg0fAdcjCo6FFHwQgYNC9zrm6NObSCabyX_5dtJkE,11074
86
+ flowstate/term.py,sha256=NjrYMk5gZjF505NFpTJaEcMGCjJkHKhLETg3-EMPvsE,2021
87
+ flowstate/tool_labels.py,sha256=eKyuxTjkwfWP_JvePeFcMOcndtpcCcHSns6p4Kt6mYw,5849
88
+ flowstate/tooldefs.py,sha256=QXKOFyrWn6QgFetRcNH2HMjj_Raip5JiVqWevuDCMB4,22188
89
+ flowstate/updates.py,sha256=thVuZipaunMo717WC49qZhX2dYcZ4bFb74GSRcU93cs,20328
90
+ flowstate/urls.py,sha256=sw1ckqCJFeunIN8yT-NhIQNAFh6gieaYvF0RkgfsUjQ,334
91
+ flowstate/waiting.py,sha256=Ppi0WZhPJzC_t6qzpNTGZ_EkO0xbmZJohAJ5yHc4sCo,42008
92
+ flowstate/who.py,sha256=NWEoeV8B3adWP-WPI3ulLnSIB2pvbH7SBnLn1B5NHbg,16009
93
+ flowstate/migrations/001_auto_sessions.sql,sha256=mwVa4hxdYEbaZTVMfuiUn8F545HTVDyLheVQAqVL4IA,915
94
+ flowstate/migrations/002_review_queue.sql,sha256=DPTFULR1_IxEUFBHELmGydrWvbsoJB1YMpQKrSe32lE,1967
95
+ flowstate/migrations/003_links.sql,sha256=6uIqrm8wSwJQGxLQUAfBLxQhkzR7OWfSjgnCsnKoCtU,2683
96
+ flowstate/migrations/004_review_mode.sql,sha256=cTb3HNGGMv2pSfIautNrHVpUwn_kfxWAQ6vhSU62rxs,1124
97
+ flowstate/migrations/005_history.sql,sha256=n3jum_P7sQSA_hmh4QHjO_iGAF6IWyFNj1aaCaRjnOM,1159
98
+ flowstate/migrations/006_activity.sql,sha256=t6xk7vF1-W6JoS5Ai4ofaI_gkNQmkw-3n9ckh9h2-DQ,1200
99
+ flowstate/migrations/007_meaning_index.sql,sha256=Wc0eYw69zspFA1fjb-tVtxnBA9luWeSby6PsX2xbKdQ,916
100
+ flowstate/migrations/008_git_capture.sql,sha256=5nsldVzztZtjxxcPW0osb4ftklheDp-7Hbz38FNnlPw,602
101
+ flowstate/migrations/009_hard_rules.sql,sha256=xStRe0VCU6GHN7a1AZSGTkpSqPtioq_4-tV6DM3fJv4,680
102
+ flowstate/migrations/010_handoffs.sql,sha256=DVezwjuiKVAUKDCpb0Yzs1Yy1TfNLKSlj0Im5GwibGA,671
103
+ flowstate/migrations/011_record_meta.sql,sha256=KD1X-bAkLZv-PdUCRjqUEXZK4Z8s5LvdMxBgoNS1z7k,812
104
+ flowstate/migrations/012_accountability.sql,sha256=0Gq9shqM2Vncg2CUJFO08cCk28Z6gTyZ1oLsHylogUU,4242
105
+ flowstate/migrations/013_todo_claims.sql,sha256=f9M1TY-4POlL26crEc2OA5rQlhhHVhFKaqhnomZ3ilE,1505
106
+ flowstate/migrations/014_decision_conflicts.sql,sha256=uCR6pvujT7O0asUtP4h4H7U851umIQkjxmDgnnNajAA,2288
107
+ flowstate/migrations/015_session_runs.sql,sha256=jYk5YGkMQY8RxzUh6Sn6d81pydR1JjZ6sFItUCNSey8,1016
108
+ flowstate/migrations/016_write_receipts.sql,sha256=RA34y9ZoOZ1BaCUGxeqJIdvbLGy41H2EjIUiQlOpIkI,1769
109
+ flowstate/migrations/017_run_views.sql,sha256=OE0DrwxVTIsPHLOBRMWUtKfMfE3mNE60B4pxVWHivls,1301
110
+ flowstate/migrations/018_run_views_upto.sql,sha256=tMhT-tdlY3fwxwPXWRzfzILTHnjAhsCARLtFqyE6Csw,338
111
+ flowstate/migrations/019_approvals.sql,sha256=QS6mA5cXJ4Gdmgbgb_pl1HEeh3E2Smz0OU8TKGJE8JU,1916
112
+ flowstate/migrations/020_source_stamps.sql,sha256=BHJfz4ma65o5szS5Qbl-rWsT-GtDCdIivPvHmo8CB4s,764
113
+ flowstate/migrations/021_review_links.sql,sha256=2GnIiRP4ckaJK-9p0NAwqlgu6Wi-Mm4ovN_VvPDLSM4,646
114
+ flowstate/migrations/022_review_start_tickets.sql,sha256=G1vNvFMoWLJzbqp4FNhnyYI1MGxzScfa_2zYN0tiYYM,796
115
+ flowstate/migrations/023_conflict_cards.sql,sha256=PuLGIc8-ChNNqxhsHz9_ONypy6KvOCm-hCINFbsvdlc,2089
116
+ flowstate/migrations/024_rule_confirmations.sql,sha256=BXuYvtgqA5sv3cDPh2gnlNMZC_RNmqzLKR7eMtvHcsw,513
117
+ flowstate/migrations/025_confirmed_hash.sql,sha256=mzpbqWQkxm7ONgrbDaQY_Pz-5czMNShkJ-B6gyIiO-o,565
118
+ flowstate/migrations/__init__.py,sha256=SHxtjurrHdLBXBb2sAPyfus60SwhmiIRHVm1vqmdDMk,5964
119
+ flowstate/relay/__init__.py,sha256=Mob9HYojjcurqQPcTqM0vlEnOT_NBgQtVspfoGWHDE8,3363
120
+ flowstate/relay/acme.py,sha256=as72VQqq3Neb8GOngsxUKRRNxMids8enQuj2SWYBGk0,11693
121
+ flowstate/relay/api.py,sha256=nxJ3Pkef70oRXPZr8BRus0QfQTB4itOsx42fWGHlH80,7703
122
+ flowstate/relay/cli.py,sha256=F6lvMiceJaslnlE-_S6z7_kHZSC_eP7PONiUiWnWkto,14792
123
+ flowstate/relay/crypto.py,sha256=xwwkUbh1TKGb_jaurlkn2wqDQYefZYjNaQls7iG3YdY,5295
124
+ flowstate/relay/ct.py,sha256=nE0cifxkoL1u7-LNCAK-HWnJEF0r30PbpRtkPe7A8cE,5104
125
+ flowstate/relay/runner.py,sha256=xFaCDY7ZPL6Y5WZnnCaj_9tJ-3U_xLogM0UF1waOQlQ,7440
126
+ flowstate/relay/store.py,sha256=093IeUgzxJEbS9fubGlzZ4nVkDUXW--_z1rGIivbFIs,7567
127
+ flowstate/relay/tunnel.py,sha256=ONZUWWY32LPiAV-p-ieocA7LZ27bICjQHz-K4o070K8,17004
128
+ flowstate/tools/__init__.py,sha256=hItuhHfcjGc4t70GEVzHS1Nzt3Xl_HCp3tvpqc8V3nE,4097
129
+ flowstate/tools/core.py,sha256=Q62YPDrnwVHQ-Df8rEe_nXhO9wr_ns6qAlshtO6gpWg,16264
130
+ flowstate/tools/files.py,sha256=T8XcDpSo00zZSmk1P6a3fMIjrqRYhM4pNQ8MvO19rZo,15852
131
+ flowstate/tools/git_ops.py,sha256=el6KIhQf10CHXAPKL7w8ypANrLyVMz_7UvHd0ImTFrk,49044
132
+ flowstate/tools/intelligence.py,sha256=B_fLO6eWMtc3aMlbdTainwlN4tG3xAD7tyA4Cjsk6h0,10486
133
+ flowstate/tools/learning.py,sha256=RnVP9-qvaB_HcKsfYJUsCwAo7vPxsRtimIcNWaW51VQ,8088
134
+ flowstate/tools/problems.py,sha256=d_mluuEE10UgrrhluN84TkiAQVOGfobvjJPolyIQilg,4473
135
+ flowstate/tools/sessions.py,sha256=Y5lehaVpDmRkmrC1F6tBQB8pRZ572zkB2KhmzhwZpw4,7150
136
+ flowstate/tools/story.py,sha256=rp0-Za72Hs5ohQSfNPQfChT24ei3xc7PjxyHwTalqas,1287
137
+ flowstate/tools/todos.py,sha256=CSm9bjbZ7d6-PPbDsUHcmg_KwjSYU1SwVUIBQi_VdoQ,2074
138
+ flowstate/tools/utils.py,sha256=6TR3OBSQ3x31I0NGgZNM8oMvrEzeFyXzNVustmqSVRU,2224
139
+ flowstate/tools/variables.py,sha256=NMK9d8_P4YBoH56ON1j8aoYIevZLu3J9q6918jHocB0,9710
140
+ flowstatemcp-2.5.0.dist-info/METADATA,sha256=JWUUduMUnX0zXFuAjoOO_bopqLR-qyMiMQ8ipEesNyo,3398
141
+ flowstatemcp-2.5.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
142
+ flowstatemcp-2.5.0.dist-info/entry_points.txt,sha256=FgrKEw4VlR4l8k3G-gOHuqQKHnlKoXtDKqFdQsB-KYQ,89
143
+ flowstatemcp-2.5.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ flowstate = flowstate.server:main
3
+ flowstatemcp = flowstate.server:main