flowforge-map 0.3.0__tar.gz → 0.4.1__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 (165) hide show
  1. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/.gitignore +7 -0
  2. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/PKG-INFO +114 -25
  3. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/README.md +111 -24
  4. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/agent/server.py +166 -25
  5. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/agent.py +40 -19
  6. flowforge_map-0.4.1/backend/flowforge/api/apps.py +253 -0
  7. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/checks.py +19 -6
  8. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/keys.py +93 -4
  9. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/lock.py +9 -4
  10. flowforge_map-0.4.1/backend/flowforge/api/projects.py +140 -0
  11. flowforge_map-0.4.1/backend/flowforge/api/settings.py +204 -0
  12. flowforge_map-0.4.1/backend/flowforge/apps.py +151 -0
  13. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/checks/probes.py +8 -1
  14. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/cli.py +95 -26
  15. flowforge_map-0.4.1/backend/flowforge/cli_ui.py +208 -0
  16. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/home.py +18 -1
  17. flowforge_map-0.4.1/backend/flowforge/main.py +184 -0
  18. flowforge_map-0.4.1/backend/flowforge/project_states.py +126 -0
  19. flowforge_map-0.4.1/backend/flowforge/projects.py +158 -0
  20. flowforge_map-0.4.1/backend/flowforge/security/finders.py +370 -0
  21. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/keystore.py +71 -12
  22. flowforge_map-0.4.1/backend/flowforge/security/local_only.py +68 -0
  23. flowforge_map-0.4.1/backend/flowforge/security/masks.py +40 -0
  24. flowforge_map-0.4.1/backend/flowforge/security/wire.py +645 -0
  25. flowforge_map-0.4.1/backend/flowforge/settings.py +46 -0
  26. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/state.py +11 -4
  27. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/storage.py +15 -0
  28. flowforge_map-0.4.1/backend/flowforge/terminal.py +134 -0
  29. flowforge_map-0.4.1/frontend/dist/assets/AddConnector-Bi8J4U4d.js +1 -0
  30. flowforge_map-0.4.1/frontend/dist/assets/Catalog-CA9b5nAc.css +1 -0
  31. flowforge_map-0.4.1/frontend/dist/assets/Catalog-zd0ADYry.js +1 -0
  32. flowforge_map-0.4.1/frontend/dist/assets/ChecksPage-B77bP82v.js +1 -0
  33. flowforge_map-0.4.1/frontend/dist/assets/Connector-D4bc9fm5.js +1 -0
  34. flowforge_map-0.4.1/frontend/dist/assets/ExportPage-X83lvpEi.js +34 -0
  35. flowforge_map-0.4.1/frontend/dist/assets/ExportPage-e2bor7CC.css +1 -0
  36. flowforge_map-0.4.1/frontend/dist/assets/FlowMapPage-BrnQhApb.js +2 -0
  37. flowforge_map-0.4.1/frontend/dist/assets/Health-DROS3rFg.js +1 -0
  38. flowforge_map-0.4.1/frontend/dist/assets/Health-FLn--j7D.css +1 -0
  39. flowforge_map-0.4.1/frontend/dist/assets/HowDrawn-CazyemwU.js +1 -0
  40. flowforge_map-0.4.1/frontend/dist/assets/IssuesPage-DI9W3uIc.js +1 -0
  41. flowforge_map-0.4.1/frontend/dist/assets/KeysPage-B2vcuxeq.js +1 -0
  42. flowforge_map-0.4.1/frontend/dist/assets/Later-89_57i-0.js +1 -0
  43. flowforge_map-0.4.1/frontend/dist/assets/LiveRun-DgaIy_Hy.js +1 -0
  44. flowforge_map-0.4.1/frontend/dist/assets/LiveRun-NyTlzZhk.css +1 -0
  45. flowforge_map-0.4.1/frontend/dist/assets/Recheck-IaxmWKc4.js +1 -0
  46. flowforge_map-0.4.1/frontend/dist/assets/RunHistory-DeusCIFP.js +1 -0
  47. flowforge_map-0.4.1/frontend/dist/assets/SettingsPage-CF0lFy0X.js +2 -0
  48. flowforge_map-0.4.1/frontend/dist/assets/SettingsPage-DrMN0pie.css +1 -0
  49. flowforge_map-0.4.1/frontend/dist/assets/client-8TP7XnGi.js +1 -0
  50. flowforge_map-0.4.1/frontend/dist/assets/flowmap-DhjGiCkH.js +7 -0
  51. flowforge_map-0.4.1/frontend/dist/assets/flowmap-qRSp4v09.css +1 -0
  52. flowforge_map-0.4.1/frontend/dist/assets/how-Bunfi3hG.css +1 -0
  53. flowforge_map-0.4.1/frontend/dist/assets/icons-B5HLu6t-.js +1 -0
  54. flowforge_map-0.4.1/frontend/dist/assets/index-Bl5rbBs-.js +11 -0
  55. flowforge_map-0.4.1/frontend/dist/assets/index-BygBw1rS.css +1 -0
  56. flowforge_map-0.4.1/frontend/dist/assets/links-BKyTYkee.js +4 -0
  57. flowforge_map-0.4.1/frontend/dist/assets/mapId-CAn3Vwt1.js +1 -0
  58. flowforge_map-0.4.1/frontend/dist/assets/sse-Bbzu7IqU.js +1 -0
  59. flowforge_map-0.4.1/frontend/dist/assets/status-BtyVLBUd.js +1 -0
  60. flowforge_map-0.4.1/frontend/dist/assets/useChecks-Dgca8Xe7.js +1 -0
  61. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/index.html +5 -2
  62. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/pyproject.toml +6 -1
  63. flowforge_map-0.3.0/backend/flowforge/main.py +0 -147
  64. flowforge_map-0.3.0/backend/flowforge/security/finders.py +0 -169
  65. flowforge_map-0.3.0/frontend/dist/assets/index-BmxPFjrF.js +0 -52
  66. flowforge_map-0.3.0/frontend/dist/assets/index-C9vBGBLq.css +0 -1
  67. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/LICENSE +0 -0
  68. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/__init__.py +0 -0
  69. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/agent/__init__.py +0 -0
  70. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/__init__.py +0 -0
  71. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/connectors.py +0 -0
  72. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/flowmap.py +0 -0
  73. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/issues.py +0 -0
  74. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/runs.py +0 -0
  75. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/scan.py +0 -0
  76. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/security.py +0 -0
  77. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/api/stats.py +0 -0
  78. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/checks/__init__.py +0 -0
  79. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/checks/models.py +0 -0
  80. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/__init__.py +0 -0
  81. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/catalog.py +0 -0
  82. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/models.py +0 -0
  83. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/presets.py +0 -0
  84. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/registry.py +0 -0
  85. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/runtime.py +0 -0
  86. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/connectors/secrets.py +0 -0
  87. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/example_data.py +0 -0
  88. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/__init__.py +0 -0
  89. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/advice.py +0 -0
  90. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/curly.py +0 -0
  91. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/index.py +0 -0
  92. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/order.py +0 -0
  93. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/reach.py +0 -0
  94. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flow/rewrite.py +0 -0
  95. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/__init__.py +0 -0
  96. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/diff.py +0 -0
  97. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/fixture.py +0 -0
  98. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/models.py +0 -0
  99. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/planner.py +0 -0
  100. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/service.py +0 -0
  101. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/flowmap/validate.py +0 -0
  102. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/__init__.py +0 -0
  103. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/base.py +0 -0
  104. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/check_node.py +0 -0
  105. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/http_node.py +0 -0
  106. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/llm_node.py +0 -0
  107. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/local_node.py +0 -0
  108. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/mcp_node.py +0 -0
  109. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/nodes/mock_node.py +0 -0
  110. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/__init__.py +0 -0
  111. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/facts.py +0 -0
  112. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/files.py +0 -0
  113. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/manifests.py +0 -0
  114. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/mcp_config.py +0 -0
  115. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/models.py +0 -0
  116. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/patterns.py +0 -0
  117. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/python_ast.py +0 -0
  118. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/scanner.py +0 -0
  119. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/services.py +0 -0
  120. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scan/urls.py +0 -0
  121. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/__init__.py +0 -0
  122. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/cache.py +0 -0
  123. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/critical_path.py +0 -0
  124. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/durations.py +0 -0
  125. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/executor.py +0 -0
  126. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/graph.py +0 -0
  127. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/scheduler/rate_limit.py +0 -0
  128. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/schema.py +0 -0
  129. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/__init__.py +0 -0
  130. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/findings.py +0 -0
  131. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/fixes.py +0 -0
  132. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/lock.py +0 -0
  133. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/security/logfilter.py +0 -0
  134. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/spa.py +0 -0
  135. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/flowforge/templating.py +0 -0
  136. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/workflows/diamond_mock.json +0 -0
  137. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/backend/workflows/stripe_to_razorpay.json +0 -0
  138. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/.env.example +0 -0
  139. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/.gitignore +0 -0
  140. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/.mcp.json +0 -0
  141. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/README.md +0 -0
  142. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/config.py +0 -0
  143. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/mcp_servers/currency.py +0 -0
  144. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/planner.py +0 -0
  145. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/requirements.txt +0 -0
  146. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/services/__init__.py +0 -0
  147. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/services/geo.py +0 -0
  148. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/services/llm.py +0 -0
  149. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/services/mcp_tools.py +0 -0
  150. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/services/weather.py +0 -0
  151. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/tools/packing_list.py +0 -0
  152. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/examples/trip-planner/trips/.gitkeep +0 -0
  153. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/classic.html +0 -0
  154. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  155. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  156. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  157. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  158. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  159. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  160. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  161. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  162. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  163. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  164. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  165. {flowforge_map-0.3.0 → flowforge_map-0.4.1}/frontend/dist/favicon.svg +0 -0
@@ -25,3 +25,10 @@ claude-resume.md
25
25
  desktop.ini
26
26
  .vscode/
27
27
  .idea/
28
+
29
+ # the desktop app (D42): build output
30
+ build/
31
+ desktop/src-tauri/binaries/
32
+ desktop/src-tauri/target/
33
+ desktop/node_modules/
34
+ desktop/src-tauri/gen/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowforge-map
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Map what your app depends on (LLMs, MCP servers, APIs), check their health and security, and see what could run faster. Local and free; the scan needs no AI.
5
5
  Project-URL: Homepage, https://github.com/Vishwa-Thangapandiyan/flowforge-agent-orchestrator
6
6
  Project-URL: Issues, https://github.com/Vishwa-Thangapandiyan/flowforge-agent-orchestrator/issues
@@ -26,31 +26,39 @@ Requires-Dist: jsonschema>=4.26.0
26
26
  Requires-Dist: keyring>=25
27
27
  Requires-Dist: mcp>=1.2
28
28
  Requires-Dist: openai>=1.40
29
+ Requires-Dist: packaging>=24
29
30
  Requires-Dist: pydantic>=2.8
30
31
  Requires-Dist: python-dotenv>=1.0
31
32
  Requires-Dist: python-multipart>=0.0.32
33
+ Requires-Dist: rich>=13
32
34
  Requires-Dist: segno>=1.6
33
35
  Requires-Dist: uvicorn[standard]>=0.30
34
36
  Description-Content-Type: text/markdown
35
37
 
36
38
  # FlowForge
37
39
 
38
- **An LLMOps tool for apps that already use AI: a map of how your app's LLM, MCP and API calls fit together, and a scheduler that runs them.**
40
+ **See everything your AI app depends on: whether it's up, whether it's safe, and what's slowing it down.**
39
41
 
40
- You connect the apps your project uses (LLMs, MCP servers, HTTP APIs, local commands). The **Planner** reads your repo and runs the app in test mode, then draws the flow on the home page: the calls, the decisions, what runs together, the retries and fallbacks, and a gate in front of anything that moves money or sends a message. Only apps your code actually calls go on the map, and every block cites its evidence (a `file:line` or a traced call). You read the map, edit it if you want (your edits never touch your code), and re-check it when the code changes.
42
+ ```bash
43
+ npx flowforge-map --example # a sample app's map in your browser: no keys, nothing sent
44
+ ```
41
45
 
42
- Underneath, FlowForge runs workflows as a DAG: it orders the steps, runs independent ones together under rate limits, caches repeat calls, and (on the roadmap) holds the secrets and pauses risky steps for a human to approve.
46
+ ![The dependency map: every LLM, MCP server and API the app calls, with live health](docs/images/map.png)
43
47
 
44
- What makes it different: **it knows which chain of steps is the bottleneck and schedules around it.**
48
+ | | |
49
+ |---|---|
50
+ | 🗺 **Map** | Every LLM, MCP server, HTTP API and local program your code calls, each with the `file:line` it came from |
51
+ | 💚 **Check now** | Green, amber or red for each one, with its latency. When something is down, it tells you why |
52
+ | ⚡ **Faster** | Reads the order your code makes its calls and shows which ones could run together, with the time you'd save |
53
+ | 🛡 **Issues** | Keys written in code, plain `http://`, MCP servers that aren't pinned. Fix one on a new branch, or send it to your agent |
54
+ | 🔑 **Keys** | Paste a key once. **Add to code** writes it to a git-ignored file your app reads; FlowForge only ever hands your agent a mask |
55
+ | 🤖 **Your agent** | One MCP entry gives Claude Code, Codex or Antigravity the map, the advice and the issues |
45
56
 
46
- - orders steps with **topological sort** (Kahn's), and reports the exact loop if there is a cycle
47
- - finds the bottleneck chain with a **longest-path DP** (critical path), predicted before the run and measured after it
48
- - runs independent steps concurrently under limited resources (`k` slots + per-provider rate limits) using **critical-path list scheduling** (HLFET), which is within `(2 − 1/k)` of optimal (Graham's bound)
49
- - makes duplicate calls only once through **memoization** (single-flight in-run cache + a safe persistent cache)
57
+ ![Flow: 16.5 s as written, 11.6 s if four calls start together](docs/images/flow.png)
50
58
 
51
- **Free by default.** The default install, the tests and CI need no paid service: LLM steps use NVIDIA NIM's free tier, and local models (Ollama) are planned. Paid providers will work if you bring your own key, but are never needed.
59
+ **Free and local.** MIT, no account, no paid service. FlowForge keeps its data on your machine, and the scan is plain code that sends nothing to any LLM.
52
60
 
53
- **Local first.** One process, SQLite, files on disk. No cloud account, no Docker, no Redis.
61
+ **Under the hood:** a DAG scheduler runs the checks concurrently under rate limits. It uses a topological sort, a critical-path DP and HLFET list scheduling (within `(2 − 1/k)` of optimal), and it caches repeat calls. See [Scheduling policies and benchmarks](#scheduling-policies-and-benchmarks).
54
62
 
55
63
  > FlowForge started as a Design and Analysis of Algorithms course project. That version is frozen on the [`DAA`](../../tree/DAA) branch. `main` is the product.
56
64
 
@@ -59,12 +67,52 @@ What makes it different: **it knows which chain of steps is the bottleneck and s
59
67
  ### Install
60
68
 
61
69
  ```bash
62
- uvx flowforge-map --example # try it: a sample app's maps in your browser, no keys, nothing sent
63
- pip install flowforge-map # or install it (the command is `flowforge`)
64
- flowforge --repo ~/my-app # map your own repo
70
+ npx flowforge-map --example # try it: a sample app's maps in your browser, no keys, nothing sent
71
+ npx flowforge-map --repo . # map the folder you're in
65
72
  ```
66
73
 
67
- You need Python 3.12 or newer ([uv](https://docs.astral.sh/uv/) installs it for you). The dashboard is inside the package: no Node needed. Your data lives in `~/.flowforge`.
74
+ That's all: no Python, no virtual env. The first run downloads FlowForge's ready-made program for your computer (about 60 MB, checked against the release's checksums) into `~/.flowforge/bin`; after that it starts at once, even offline. Works on Windows, macOS (Apple chips) and Linux x64 with Node 18+. Your data lives in `~/.flowforge`.
75
+
76
+ | You have | Run | |
77
+ |---|---|---|
78
+ | Node (most developers) | `npx flowforge-map` | ⭐ no Python needed |
79
+ | Node, every day | `npm i -g flowforge-map`, then just `flowforge` | installs the launcher; the program comes on first run |
80
+ | Python people | `uvx flowforge-map` or `pipx install flowforge-map` | Python 3.12+; no virtual env to manage |
81
+ | No terminal | the desktop app (Releases) | download and open; **Settings → Terminal** adds the `flowforge` command too (Linux .rpm/.deb have it already) |
82
+
83
+ Every way gives the same `flowforge` command: `npx flowforge-map status`, `npx flowforge-map key add STRIPE_KEY`, and so on. Intel Macs, Linux ARM and Windows ARM have no ready-made build yet, so `npx` uses `uvx` there.
84
+
85
+ The first time, a short welcome walks you through three steps, each one skippable: your app's folder (or the example), pairing your coding agent once, and a lock for your keys. **⚙ Settings** at the bottom of the rail keeps what applies to every project: your agent, the theme and map style, the Vault's lock, and your data (each project's size, the MCP servers it may start, its check history, and the version, with **Check for updates**, which asks pypi.org only when you press it).
86
+
87
+ ### Terminal
88
+
89
+ ```bash
90
+ flowforge status [PROJECT] # health, issues, missing keys and the next step
91
+ flowforge projects # every project at a glance
92
+ flowforge issues [PROJECT] # security findings and fixes
93
+ flowforge check [PROJECT] # preview checks; run them from the dashboard
94
+ flowforge settings # saved settings, data location and version
95
+ flowforge scan ~/my-app # terminal summary; pipe it or use --json for JSON
96
+ flowforge key list # key names and statuses, never values
97
+ ```
98
+
99
+ Project commands use the current folder's project, else the last opened; PROJECT is its name or id. They read local files and work with the dashboard stopped. `--plain` or `NO_COLOR` disables colour and the banner; pipes have no colour or animation.
100
+
101
+ ### Desktop app
102
+
103
+ The same dashboard in its own window, with Python inside: nothing else to install. Download the installer for your computer from the [Releases](https://github.com/Vishwa-Thangapandiyan/flowforge-agent-orchestrator/releases) page:
104
+
105
+ | Your computer | Download |
106
+ |---|---|
107
+ | Windows | `FlowForge_x.y.z_x64-setup.exe` or `.msi` |
108
+ | Mac (Apple Silicon or Intel) | `FlowForge_x.y.z_aarch64.dmg` or `_x64.dmg` |
109
+ | Linux | `.AppImage`, `.deb` or `.rpm` |
110
+
111
+ The builds aren't signed yet (signing costs money every year), so the first launch asks once:
112
+ - **Mac:** right-click FlowForge in Applications → **Open** → **Open**.
113
+ - **Windows:** on "Windows protected your PC", click **More info** → **Run anyway**.
114
+
115
+ After that it opens normally. **Browse…** picks your app's folder; **Settings → Your agent** pairs your coding agent with the app's own FlowForge.
68
116
 
69
117
  ### From source (to work on FlowForge)
70
118
 
@@ -83,7 +131,7 @@ uv run flowforge --example # opens http://127.0.0.1:8000: a sampl
83
131
  ### Map your own repo
84
132
 
85
133
  ```bash
86
- uv run flowforge scan path/to/your/repo # what it depends on, as JSON: nothing stored, nothing sent
134
+ uv run flowforge scan path/to/your/repo # summary (pipe or --json for JSON); nothing stored, nothing sent
87
135
  uv run flowforge --repo path/to/your/repo # opens its dependency map; add --replace to switch folders
88
136
  ```
89
137
 
@@ -105,19 +153,24 @@ Switch the map to **Flow**. FlowForge reads your Python (by its syntax tree), Da
105
153
  - **Faster** compares "as written" (the slowest action, one call after another) with "could be", and lists the calls that don't need each other's results. **Show the change** writes out the `asyncio.gather`, Dart's `(a, b).wait` or `Promise.all` (now → could be, ready to copy) when it's safe; **Copy prompt for your coding agent** hands the rest to Claude Code, Codex or any agent. FlowForge never edits your code.
106
154
  - **Watch it run** plays the could-be schedule; pointing at a block lights up its path. Five map styles: Studio, Boxed, Blueprint, Metro, Soft.
107
155
 
156
+ ### Many projects
157
+
158
+ The home page lists every app you've mapped, with its dependencies, what needs fixing and missing keys. **+ New project** takes your app's folder, scans it and shows its map. The project tile at the top of the rail switches between the projects you have open, and each one comes back where you left it. **×** closes a project from that list without deleting it. Each project keeps its own map, checks, issues and keys; `flowforge --repo PATH` opens (or adds) that folder's project.
108
159
 
109
160
  ### Use it from your coding agent
110
161
 
111
- FlowForge is also an **MCP server**, so Claude Code, Codex or Antigravity can ask it about your app and then make the change themselves. Open **Connect agent** in the dashboard: it gives you the one line (or config block) for your agent, with the paths filled in. For Claude Code it looks like:
162
+ FlowForge is also an **MCP server**, so Claude Code, Codex or Antigravity can ask it about your app and then make the change themselves. **Settings → Your agent** (also on **Connect agent**) gives you the one line (or config block) for your agent. With `uvx`, for Claude Code it looks like:
112
163
 
113
164
  ```bash
114
- claude mcp add flowforge -- uv --directory /path/to/flowforge run flowforge mcp --repo /path/to/your/app
165
+ claude mcp add flowforge -- uvx flowforge-map mcp
115
166
  ```
116
167
 
117
- Your agent gets ten tools: `get_map`, `get_flow`, `get_advice`, `check_health`, `get_security_findings`, `get_keys`, `get_fix_requests`, `request_key`, `wait_for_keys` and `what_changed`. All read only, except `request_key`, which only asks you for a key by name. Every answer is masked (key values never appear), the agent can't make FlowForge change your code, and a check starts an MCP server only after you said yes to it on the Checks page. FlowForge needs no AI of its own for any of this.
168
+ Pair your agent once and it sees every project: `flowforge mcp` with no folder answers about the project your agent is working in, or any other by name (`list_projects` shows them all). Your agent gets eleven tools: `list_projects`, `get_map`, `get_flow`, `get_advice`, `check_health`, `get_security_findings`, `get_keys`, `get_fix_requests`, `request_key`, `wait_for_keys` and `what_changed`. All read only, except `request_key`, which only asks you for a key by name. Every answer is masked (key values never appear), the agent can't make FlowForge change your code, and a check starts an MCP server only after you said yes to it on the Checks page. FlowForge needs no AI of its own for any of this.
118
169
 
119
170
  ### Fix what's unsafe, with one click
120
171
 
172
+ ![Issues: a key in the code, an unpinned MCP server, plain http](docs/images/issues.png)
173
+
121
174
  The **Issues** page lists what the scan found unsafe, and every issue has one button:
122
175
 
123
176
  | Issue | Button | What happens |
@@ -129,22 +182,55 @@ Fix needs a git repo and the file committed. FlowForge works out the change itse
129
182
 
130
183
  ### Keys your coding agent never sees
131
184
 
132
- Your agent only needs a key's **name**: the code it writes reads `os.environ["STRIPE_SECRET_KEY"]`. You give FlowForge the value once, and your app gets it when it starts:
185
+ ![Keys: paste once, Add to code, the agent sees a mask](docs/images/keys.png)
133
186
 
187
+ **Add to code.** The **Keys** page lists each place your code needs a key, with its `file:line`. Paste the key and press **Add to code**:
188
+
189
+ ```
190
+ lib/config.dart:8 String.fromEnvironment('SUPABASE_ANON_KEY') ← FlowForge found the spot
191
+ [ •••••••••••• ] [ Add to code ] ← you paste, one click
192
+ ✓ SUPABASE_ANON_KEY is in your app. Press Run as always.
193
+ Changed: .flowforge/dart-defines.json · Run config · .gitignore · Claude Code told not to read it · Vault
194
+ ```
195
+
196
+ The key goes where your app reads it, and **never into a code file**:
197
+
198
+ | Your app | The key goes to | Then |
199
+ |---|---|---|
200
+ | Flutter (`String.fromEnvironment`) | `.flowforge/dart-defines.json`, and `--dart-define-from-file` is added to your Run config (Android Studio, IntelliJ, VS Code) | press Run |
201
+ | Python, Node or Flutter with a `.env` loader | `.env` | start it as always |
202
+ | Vite | `.env.local` (as `VITE_…`) | start it as always |
203
+
204
+ - A placeholder like `apiKey = "ENTER KEY HERE"` is rewritten to read the key.
205
+ - A real key written in the code gets **Hide it**: one click moves it out the same way.
206
+ - The file is git-ignored, and nothing is committed: you see the change in `git diff`.
207
+ - An app that loads no file gets **Save**, and the Keys page says why.
208
+ - The settings a key needs come too: `SUPABASE_URL` is listed under **Your app also needs**, filled in from your Supabase key, with the same **Add to code** (a setting isn't secret, so it doesn't go to the Vault).
209
+
210
+ **With an agent**, the agent never gets a key; it gets a **mask**:
211
+
212
+ ```
213
+ you paste STRIPE_SECRET_KEY once → your agent sees ffkey://STRIPE_SECRET_KEY
214
+ the agent writes the mask where the key goes (or reads the env var STRIPE_SECRET_KEY in code)
215
+ the agent calls run_app(["flutter", "run", "--dart-define=STRIPE_SECRET_KEY=ffkey://STRIPE_SECRET_KEY"])
216
+ FlowForge asks you once: "Your agent wants to run flutter run … with STRIPE_SECRET_KEY" [Deny] [Allow]
217
+ FlowForge (plain code, no AI) fills the mask and starts the app → the output comes back masked
218
+ ```
219
+
220
+ Masks work in commands, in the environment and in `.env` lines; other config files never get the key written into them, so read the env var there. After you allow a command once, the same command runs without asking (**Settings** can turn the question off). The Keys page lists your agent's runs with **Stop**. This covers keys read when the app is *built* too, such as Flutter's `--dart-define` and Vite's `import.meta.env`. On the map, a 🔑 badge marks a block whose key is missing. With no keychain (a server, CI), keys go to an encrypted file in `~/.flowforge`.
221
+
222
+ **No agent?** The same works by hand:
134
223
  ```bash
135
224
  flowforge key add STRIPE_SECRET_KEY # typed hidden; saved in your OS keychain
136
225
  flowforge key list # each key the code reads: saved · in .env · missing
137
- flowforge key import # move a .env into FlowForge (then delete the .env)
138
- flowforge run -- python app.py # your app starts with the keys in its environment
226
+ flowforge run -- python app.py # your app starts with the keys (masks filled)
139
227
  ```
140
228
 
141
- Each command takes `--repo PATH` (default: the folder you're in). In the dashboard, the **Keys** page does the same with no commands: paste a missing key into its box, or click **Move into FlowForge** for one that's in `.env`. On the map, a 🔑 badge marks a block whose key is missing. Your agent's `get_keys` tool gets names, statuses and the command to ask you to run, never a value. With no keychain (a server, CI), keys go to an encrypted file in `~/.flowforge`.
142
-
143
229
  **When your agent adds a dependency that needs a key**, it calls `request_key("STRIPE_SECRET_KEY", "for Stripe in pay.py")`. The map says "Your agent asked for a key", and the new block shows in coral, "key missing". Click it, paste the key on the Keys page, and the agent's `wait_for_keys` returns the moment it's saved. The agent only ever sees the name.
144
230
 
145
231
  **See a key again** in the **Vault**: set a password and scan the QR with an authenticator app (Google Authenticator, Authy, 1Password), once. After that, your password, then the 6-digit code on your phone, open it for 5 minutes. It lists every key with its health: healthy, written in code, in `.env`, can't read, unused, or old. It also shows where each key lives, what uses it, and whether it's a live or test key. Each value shows for 30 seconds. Forgot the password? `flowforge lock reset --forget` drops the lock (your keys stay) and the dashboard says so.
146
232
 
147
- What this does and doesn't promise: the key is never handed to your agent and isn't left in a file it reads by habit. An agent that can run commands as you could still run `flowforge run -- printenv`, read your keychain, or reset the lock (the dashboard then shows it was reset), so it isn't impossible. The 2FA stops web pages, anyone at your browser, and anything without your phone. MCP servers your coding tool starts get their environment from that tool, not from FlowForge.
233
+ What FlowForge promises: a key is never in anything your agent receives (answers, its own process, the output it returns), never in a code file, a stored row or a log. **Add to code** puts it in one hidden, git-ignored file next to your app (like `.env`), which Claude Code is told not to read; other agents could still read it. Outside FlowForge: what your own app does with a key, a key built into an app (anyone holding the app can read it, so use publishable keys there), and other programs running as your user. The 2FA on the Vault stops web pages, anyone at your browser, and anything without your phone.
148
234
 
149
235
  ### Send someone the map
150
236
 
@@ -191,6 +277,7 @@ To use real LLM steps, `cp .env.example .env` and add a free `NVIDIA_API_KEY` fr
191
277
  | **MCP Map** (Export) | The map to send: SVG, PNG, PDF, Mermaid or Flow JSON |
192
278
  | **Checks** | Check now, each dependency's verdict, why it's down and the likely fix, and the history |
193
279
  | **Connect agent** | The set-up for Claude Code, Codex or Antigravity; what your agent gets from this repo (dependencies, health, time, ways to be faster); and, once connected, what it asked |
280
+ | **Settings** (⚙, every project) | Your agent, paired once; the theme and the map style maps open in; the Vault's lock and where keys are kept (never a value); your data, each project's yeses and check history, and Check for updates |
194
281
  | **How the Planner drew it** | The analysis replayed: read the repo, run the app in test mode, trace the calls, draft, check. What it read, what it dropped and why |
195
282
  | **Re-check** | What changed in your repo since the last map: added, changed, removed, and conflicts with your edits. You pick what to take; the old version stays listed |
196
283
  | **Apps and connectors** (Map ⋯) | A catalog of 56 apps with search and categories, honest **Live** / **Demo only** badges, and a mock test run for every one. Apps you connected but your code doesn't call wait here, off the map |
@@ -271,6 +358,8 @@ FlowForge itself keeps keys out of its runs, events, cache, logs and the dashboa
271
358
  | `POST /connectors/{id}/test` | One harmless check (MCP and local apps) |
272
359
  | `GET /connectors/{id}/activity`, `GET /connectors/{id}/tools` | What an app has done; an MCP server's tools |
273
360
  | `GET /security` | The scan's security findings, by level, with the fix and a prompt for each (D31) |
361
+ | `GET /projects`, `POST /projects`, `PATCH\|DELETE /projects/{id}`, `/p/{id}/…` | Every project; add a folder; rename or remove one (its folder is never touched); any route above for one project (D39) |
362
+ | `GET\|PATCH /settings`, `DELETE /projects/{id}/checks\|confirmed`, `POST /settings/update-check` | Settings for every project (no secret in them); clear a project's check history or take back its yeses; ask pypi.org for the newest version, only when pressed (D40) |
274
363
  | `GET /keys`, `PUT /keys/{name}`, `DELETE /keys/{name}`, `POST /keys/{name}/import` | The keys the code reads and their status; save one (write-only), move one in from `.env`, or forget it (D36, D37) |
275
364
  | `GET /issues`, `POST /issues/{id}/preview\|fix\|send` | Every issue with its button; the masked change; make it on a new branch; send it to your agent (D37) |
276
365
  | `GET /lock`, `POST /lock/setup\|confirm\|password\|code\|close`, `GET /vault/keys`, `POST /keys/reveal` | The Vault: its lock (your password, then the authenticator code) and, once open, every key's health and one value at a time (D37, D38) |
@@ -1,21 +1,27 @@
1
1
  # FlowForge
2
2
 
3
- **An LLMOps tool for apps that already use AI: a map of how your app's LLM, MCP and API calls fit together, and a scheduler that runs them.**
3
+ **See everything your AI app depends on: whether it's up, whether it's safe, and what's slowing it down.**
4
4
 
5
- You connect the apps your project uses (LLMs, MCP servers, HTTP APIs, local commands). The **Planner** reads your repo and runs the app in test mode, then draws the flow on the home page: the calls, the decisions, what runs together, the retries and fallbacks, and a gate in front of anything that moves money or sends a message. Only apps your code actually calls go on the map, and every block cites its evidence (a `file:line` or a traced call). You read the map, edit it if you want (your edits never touch your code), and re-check it when the code changes.
5
+ ```bash
6
+ npx flowforge-map --example # a sample app's map in your browser: no keys, nothing sent
7
+ ```
6
8
 
7
- Underneath, FlowForge runs workflows as a DAG: it orders the steps, runs independent ones together under rate limits, caches repeat calls, and (on the roadmap) holds the secrets and pauses risky steps for a human to approve.
9
+ ![The dependency map: every LLM, MCP server and API the app calls, with live health](docs/images/map.png)
8
10
 
9
- What makes it different: **it knows which chain of steps is the bottleneck and schedules around it.**
11
+ | | |
12
+ |---|---|
13
+ | 🗺 **Map** | Every LLM, MCP server, HTTP API and local program your code calls, each with the `file:line` it came from |
14
+ | 💚 **Check now** | Green, amber or red for each one, with its latency. When something is down, it tells you why |
15
+ | ⚡ **Faster** | Reads the order your code makes its calls and shows which ones could run together, with the time you'd save |
16
+ | 🛡 **Issues** | Keys written in code, plain `http://`, MCP servers that aren't pinned. Fix one on a new branch, or send it to your agent |
17
+ | 🔑 **Keys** | Paste a key once. **Add to code** writes it to a git-ignored file your app reads; FlowForge only ever hands your agent a mask |
18
+ | 🤖 **Your agent** | One MCP entry gives Claude Code, Codex or Antigravity the map, the advice and the issues |
10
19
 
11
- - orders steps with **topological sort** (Kahn's), and reports the exact loop if there is a cycle
12
- - finds the bottleneck chain with a **longest-path DP** (critical path), predicted before the run and measured after it
13
- - runs independent steps concurrently under limited resources (`k` slots + per-provider rate limits) using **critical-path list scheduling** (HLFET), which is within `(2 − 1/k)` of optimal (Graham's bound)
14
- - makes duplicate calls only once through **memoization** (single-flight in-run cache + a safe persistent cache)
20
+ ![Flow: 16.5 s as written, 11.6 s if four calls start together](docs/images/flow.png)
15
21
 
16
- **Free by default.** The default install, the tests and CI need no paid service: LLM steps use NVIDIA NIM's free tier, and local models (Ollama) are planned. Paid providers will work if you bring your own key, but are never needed.
22
+ **Free and local.** MIT, no account, no paid service. FlowForge keeps its data on your machine, and the scan is plain code that sends nothing to any LLM.
17
23
 
18
- **Local first.** One process, SQLite, files on disk. No cloud account, no Docker, no Redis.
24
+ **Under the hood:** a DAG scheduler runs the checks concurrently under rate limits. It uses a topological sort, a critical-path DP and HLFET list scheduling (within `(2 − 1/k)` of optimal), and it caches repeat calls. See [Scheduling policies and benchmarks](#scheduling-policies-and-benchmarks).
19
25
 
20
26
  > FlowForge started as a Design and Analysis of Algorithms course project. That version is frozen on the [`DAA`](../../tree/DAA) branch. `main` is the product.
21
27
 
@@ -24,12 +30,52 @@ What makes it different: **it knows which chain of steps is the bottleneck and s
24
30
  ### Install
25
31
 
26
32
  ```bash
27
- uvx flowforge-map --example # try it: a sample app's maps in your browser, no keys, nothing sent
28
- pip install flowforge-map # or install it (the command is `flowforge`)
29
- flowforge --repo ~/my-app # map your own repo
33
+ npx flowforge-map --example # try it: a sample app's maps in your browser, no keys, nothing sent
34
+ npx flowforge-map --repo . # map the folder you're in
30
35
  ```
31
36
 
32
- You need Python 3.12 or newer ([uv](https://docs.astral.sh/uv/) installs it for you). The dashboard is inside the package: no Node needed. Your data lives in `~/.flowforge`.
37
+ That's all: no Python, no virtual env. The first run downloads FlowForge's ready-made program for your computer (about 60 MB, checked against the release's checksums) into `~/.flowforge/bin`; after that it starts at once, even offline. Works on Windows, macOS (Apple chips) and Linux x64 with Node 18+. Your data lives in `~/.flowforge`.
38
+
39
+ | You have | Run | |
40
+ |---|---|---|
41
+ | Node (most developers) | `npx flowforge-map` | ⭐ no Python needed |
42
+ | Node, every day | `npm i -g flowforge-map`, then just `flowforge` | installs the launcher; the program comes on first run |
43
+ | Python people | `uvx flowforge-map` or `pipx install flowforge-map` | Python 3.12+; no virtual env to manage |
44
+ | No terminal | the desktop app (Releases) | download and open; **Settings → Terminal** adds the `flowforge` command too (Linux .rpm/.deb have it already) |
45
+
46
+ Every way gives the same `flowforge` command: `npx flowforge-map status`, `npx flowforge-map key add STRIPE_KEY`, and so on. Intel Macs, Linux ARM and Windows ARM have no ready-made build yet, so `npx` uses `uvx` there.
47
+
48
+ The first time, a short welcome walks you through three steps, each one skippable: your app's folder (or the example), pairing your coding agent once, and a lock for your keys. **⚙ Settings** at the bottom of the rail keeps what applies to every project: your agent, the theme and map style, the Vault's lock, and your data (each project's size, the MCP servers it may start, its check history, and the version, with **Check for updates**, which asks pypi.org only when you press it).
49
+
50
+ ### Terminal
51
+
52
+ ```bash
53
+ flowforge status [PROJECT] # health, issues, missing keys and the next step
54
+ flowforge projects # every project at a glance
55
+ flowforge issues [PROJECT] # security findings and fixes
56
+ flowforge check [PROJECT] # preview checks; run them from the dashboard
57
+ flowforge settings # saved settings, data location and version
58
+ flowforge scan ~/my-app # terminal summary; pipe it or use --json for JSON
59
+ flowforge key list # key names and statuses, never values
60
+ ```
61
+
62
+ Project commands use the current folder's project, else the last opened; PROJECT is its name or id. They read local files and work with the dashboard stopped. `--plain` or `NO_COLOR` disables colour and the banner; pipes have no colour or animation.
63
+
64
+ ### Desktop app
65
+
66
+ The same dashboard in its own window, with Python inside: nothing else to install. Download the installer for your computer from the [Releases](https://github.com/Vishwa-Thangapandiyan/flowforge-agent-orchestrator/releases) page:
67
+
68
+ | Your computer | Download |
69
+ |---|---|
70
+ | Windows | `FlowForge_x.y.z_x64-setup.exe` or `.msi` |
71
+ | Mac (Apple Silicon or Intel) | `FlowForge_x.y.z_aarch64.dmg` or `_x64.dmg` |
72
+ | Linux | `.AppImage`, `.deb` or `.rpm` |
73
+
74
+ The builds aren't signed yet (signing costs money every year), so the first launch asks once:
75
+ - **Mac:** right-click FlowForge in Applications → **Open** → **Open**.
76
+ - **Windows:** on "Windows protected your PC", click **More info** → **Run anyway**.
77
+
78
+ After that it opens normally. **Browse…** picks your app's folder; **Settings → Your agent** pairs your coding agent with the app's own FlowForge.
33
79
 
34
80
  ### From source (to work on FlowForge)
35
81
 
@@ -48,7 +94,7 @@ uv run flowforge --example # opens http://127.0.0.1:8000: a sampl
48
94
  ### Map your own repo
49
95
 
50
96
  ```bash
51
- uv run flowforge scan path/to/your/repo # what it depends on, as JSON: nothing stored, nothing sent
97
+ uv run flowforge scan path/to/your/repo # summary (pipe or --json for JSON); nothing stored, nothing sent
52
98
  uv run flowforge --repo path/to/your/repo # opens its dependency map; add --replace to switch folders
53
99
  ```
54
100
 
@@ -70,19 +116,24 @@ Switch the map to **Flow**. FlowForge reads your Python (by its syntax tree), Da
70
116
  - **Faster** compares "as written" (the slowest action, one call after another) with "could be", and lists the calls that don't need each other's results. **Show the change** writes out the `asyncio.gather`, Dart's `(a, b).wait` or `Promise.all` (now → could be, ready to copy) when it's safe; **Copy prompt for your coding agent** hands the rest to Claude Code, Codex or any agent. FlowForge never edits your code.
71
117
  - **Watch it run** plays the could-be schedule; pointing at a block lights up its path. Five map styles: Studio, Boxed, Blueprint, Metro, Soft.
72
118
 
119
+ ### Many projects
120
+
121
+ The home page lists every app you've mapped, with its dependencies, what needs fixing and missing keys. **+ New project** takes your app's folder, scans it and shows its map. The project tile at the top of the rail switches between the projects you have open, and each one comes back where you left it. **×** closes a project from that list without deleting it. Each project keeps its own map, checks, issues and keys; `flowforge --repo PATH` opens (or adds) that folder's project.
73
122
 
74
123
  ### Use it from your coding agent
75
124
 
76
- FlowForge is also an **MCP server**, so Claude Code, Codex or Antigravity can ask it about your app and then make the change themselves. Open **Connect agent** in the dashboard: it gives you the one line (or config block) for your agent, with the paths filled in. For Claude Code it looks like:
125
+ FlowForge is also an **MCP server**, so Claude Code, Codex or Antigravity can ask it about your app and then make the change themselves. **Settings → Your agent** (also on **Connect agent**) gives you the one line (or config block) for your agent. With `uvx`, for Claude Code it looks like:
77
126
 
78
127
  ```bash
79
- claude mcp add flowforge -- uv --directory /path/to/flowforge run flowforge mcp --repo /path/to/your/app
128
+ claude mcp add flowforge -- uvx flowforge-map mcp
80
129
  ```
81
130
 
82
- Your agent gets ten tools: `get_map`, `get_flow`, `get_advice`, `check_health`, `get_security_findings`, `get_keys`, `get_fix_requests`, `request_key`, `wait_for_keys` and `what_changed`. All read only, except `request_key`, which only asks you for a key by name. Every answer is masked (key values never appear), the agent can't make FlowForge change your code, and a check starts an MCP server only after you said yes to it on the Checks page. FlowForge needs no AI of its own for any of this.
131
+ Pair your agent once and it sees every project: `flowforge mcp` with no folder answers about the project your agent is working in, or any other by name (`list_projects` shows them all). Your agent gets eleven tools: `list_projects`, `get_map`, `get_flow`, `get_advice`, `check_health`, `get_security_findings`, `get_keys`, `get_fix_requests`, `request_key`, `wait_for_keys` and `what_changed`. All read only, except `request_key`, which only asks you for a key by name. Every answer is masked (key values never appear), the agent can't make FlowForge change your code, and a check starts an MCP server only after you said yes to it on the Checks page. FlowForge needs no AI of its own for any of this.
83
132
 
84
133
  ### Fix what's unsafe, with one click
85
134
 
135
+ ![Issues: a key in the code, an unpinned MCP server, plain http](docs/images/issues.png)
136
+
86
137
  The **Issues** page lists what the scan found unsafe, and every issue has one button:
87
138
 
88
139
  | Issue | Button | What happens |
@@ -94,22 +145,55 @@ Fix needs a git repo and the file committed. FlowForge works out the change itse
94
145
 
95
146
  ### Keys your coding agent never sees
96
147
 
97
- Your agent only needs a key's **name**: the code it writes reads `os.environ["STRIPE_SECRET_KEY"]`. You give FlowForge the value once, and your app gets it when it starts:
148
+ ![Keys: paste once, Add to code, the agent sees a mask](docs/images/keys.png)
98
149
 
150
+ **Add to code.** The **Keys** page lists each place your code needs a key, with its `file:line`. Paste the key and press **Add to code**:
151
+
152
+ ```
153
+ lib/config.dart:8 String.fromEnvironment('SUPABASE_ANON_KEY') ← FlowForge found the spot
154
+ [ •••••••••••• ] [ Add to code ] ← you paste, one click
155
+ ✓ SUPABASE_ANON_KEY is in your app. Press Run as always.
156
+ Changed: .flowforge/dart-defines.json · Run config · .gitignore · Claude Code told not to read it · Vault
157
+ ```
158
+
159
+ The key goes where your app reads it, and **never into a code file**:
160
+
161
+ | Your app | The key goes to | Then |
162
+ |---|---|---|
163
+ | Flutter (`String.fromEnvironment`) | `.flowforge/dart-defines.json`, and `--dart-define-from-file` is added to your Run config (Android Studio, IntelliJ, VS Code) | press Run |
164
+ | Python, Node or Flutter with a `.env` loader | `.env` | start it as always |
165
+ | Vite | `.env.local` (as `VITE_…`) | start it as always |
166
+
167
+ - A placeholder like `apiKey = "ENTER KEY HERE"` is rewritten to read the key.
168
+ - A real key written in the code gets **Hide it**: one click moves it out the same way.
169
+ - The file is git-ignored, and nothing is committed: you see the change in `git diff`.
170
+ - An app that loads no file gets **Save**, and the Keys page says why.
171
+ - The settings a key needs come too: `SUPABASE_URL` is listed under **Your app also needs**, filled in from your Supabase key, with the same **Add to code** (a setting isn't secret, so it doesn't go to the Vault).
172
+
173
+ **With an agent**, the agent never gets a key; it gets a **mask**:
174
+
175
+ ```
176
+ you paste STRIPE_SECRET_KEY once → your agent sees ffkey://STRIPE_SECRET_KEY
177
+ the agent writes the mask where the key goes (or reads the env var STRIPE_SECRET_KEY in code)
178
+ the agent calls run_app(["flutter", "run", "--dart-define=STRIPE_SECRET_KEY=ffkey://STRIPE_SECRET_KEY"])
179
+ FlowForge asks you once: "Your agent wants to run flutter run … with STRIPE_SECRET_KEY" [Deny] [Allow]
180
+ FlowForge (plain code, no AI) fills the mask and starts the app → the output comes back masked
181
+ ```
182
+
183
+ Masks work in commands, in the environment and in `.env` lines; other config files never get the key written into them, so read the env var there. After you allow a command once, the same command runs without asking (**Settings** can turn the question off). The Keys page lists your agent's runs with **Stop**. This covers keys read when the app is *built* too, such as Flutter's `--dart-define` and Vite's `import.meta.env`. On the map, a 🔑 badge marks a block whose key is missing. With no keychain (a server, CI), keys go to an encrypted file in `~/.flowforge`.
184
+
185
+ **No agent?** The same works by hand:
99
186
  ```bash
100
187
  flowforge key add STRIPE_SECRET_KEY # typed hidden; saved in your OS keychain
101
188
  flowforge key list # each key the code reads: saved · in .env · missing
102
- flowforge key import # move a .env into FlowForge (then delete the .env)
103
- flowforge run -- python app.py # your app starts with the keys in its environment
189
+ flowforge run -- python app.py # your app starts with the keys (masks filled)
104
190
  ```
105
191
 
106
- Each command takes `--repo PATH` (default: the folder you're in). In the dashboard, the **Keys** page does the same with no commands: paste a missing key into its box, or click **Move into FlowForge** for one that's in `.env`. On the map, a 🔑 badge marks a block whose key is missing. Your agent's `get_keys` tool gets names, statuses and the command to ask you to run, never a value. With no keychain (a server, CI), keys go to an encrypted file in `~/.flowforge`.
107
-
108
192
  **When your agent adds a dependency that needs a key**, it calls `request_key("STRIPE_SECRET_KEY", "for Stripe in pay.py")`. The map says "Your agent asked for a key", and the new block shows in coral, "key missing". Click it, paste the key on the Keys page, and the agent's `wait_for_keys` returns the moment it's saved. The agent only ever sees the name.
109
193
 
110
194
  **See a key again** in the **Vault**: set a password and scan the QR with an authenticator app (Google Authenticator, Authy, 1Password), once. After that, your password, then the 6-digit code on your phone, open it for 5 minutes. It lists every key with its health: healthy, written in code, in `.env`, can't read, unused, or old. It also shows where each key lives, what uses it, and whether it's a live or test key. Each value shows for 30 seconds. Forgot the password? `flowforge lock reset --forget` drops the lock (your keys stay) and the dashboard says so.
111
195
 
112
- What this does and doesn't promise: the key is never handed to your agent and isn't left in a file it reads by habit. An agent that can run commands as you could still run `flowforge run -- printenv`, read your keychain, or reset the lock (the dashboard then shows it was reset), so it isn't impossible. The 2FA stops web pages, anyone at your browser, and anything without your phone. MCP servers your coding tool starts get their environment from that tool, not from FlowForge.
196
+ What FlowForge promises: a key is never in anything your agent receives (answers, its own process, the output it returns), never in a code file, a stored row or a log. **Add to code** puts it in one hidden, git-ignored file next to your app (like `.env`), which Claude Code is told not to read; other agents could still read it. Outside FlowForge: what your own app does with a key, a key built into an app (anyone holding the app can read it, so use publishable keys there), and other programs running as your user. The 2FA on the Vault stops web pages, anyone at your browser, and anything without your phone.
113
197
 
114
198
  ### Send someone the map
115
199
 
@@ -156,6 +240,7 @@ To use real LLM steps, `cp .env.example .env` and add a free `NVIDIA_API_KEY` fr
156
240
  | **MCP Map** (Export) | The map to send: SVG, PNG, PDF, Mermaid or Flow JSON |
157
241
  | **Checks** | Check now, each dependency's verdict, why it's down and the likely fix, and the history |
158
242
  | **Connect agent** | The set-up for Claude Code, Codex or Antigravity; what your agent gets from this repo (dependencies, health, time, ways to be faster); and, once connected, what it asked |
243
+ | **Settings** (⚙, every project) | Your agent, paired once; the theme and the map style maps open in; the Vault's lock and where keys are kept (never a value); your data, each project's yeses and check history, and Check for updates |
159
244
  | **How the Planner drew it** | The analysis replayed: read the repo, run the app in test mode, trace the calls, draft, check. What it read, what it dropped and why |
160
245
  | **Re-check** | What changed in your repo since the last map: added, changed, removed, and conflicts with your edits. You pick what to take; the old version stays listed |
161
246
  | **Apps and connectors** (Map ⋯) | A catalog of 56 apps with search and categories, honest **Live** / **Demo only** badges, and a mock test run for every one. Apps you connected but your code doesn't call wait here, off the map |
@@ -236,6 +321,8 @@ FlowForge itself keeps keys out of its runs, events, cache, logs and the dashboa
236
321
  | `POST /connectors/{id}/test` | One harmless check (MCP and local apps) |
237
322
  | `GET /connectors/{id}/activity`, `GET /connectors/{id}/tools` | What an app has done; an MCP server's tools |
238
323
  | `GET /security` | The scan's security findings, by level, with the fix and a prompt for each (D31) |
324
+ | `GET /projects`, `POST /projects`, `PATCH\|DELETE /projects/{id}`, `/p/{id}/…` | Every project; add a folder; rename or remove one (its folder is never touched); any route above for one project (D39) |
325
+ | `GET\|PATCH /settings`, `DELETE /projects/{id}/checks\|confirmed`, `POST /settings/update-check` | Settings for every project (no secret in them); clear a project's check history or take back its yeses; ask pypi.org for the newest version, only when pressed (D40) |
239
326
  | `GET /keys`, `PUT /keys/{name}`, `DELETE /keys/{name}`, `POST /keys/{name}/import` | The keys the code reads and their status; save one (write-only), move one in from `.env`, or forget it (D36, D37) |
240
327
  | `GET /issues`, `POST /issues/{id}/preview\|fix\|send` | Every issue with its button; the masked change; make it on a new branch; send it to your agent (D37) |
241
328
  | `GET /lock`, `POST /lock/setup\|confirm\|password\|code\|close`, `GET /vault/keys`, `POST /keys/reveal` | The Vault: its lock (your password, then the authenticator code) and, once open, every key's health and one value at a time (D37, D38) |