feed-the-machine 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +262 -170
  3. package/bin/__pycache__/tasks_db.cpython-314.pyc +0 -0
  4. package/bin/brain.py +1340 -0
  5. package/bin/convert_claude_skills_to_codex.py +490 -0
  6. package/bin/generate-manifest.mjs +463 -463
  7. package/bin/harden_codex_skills.py +141 -0
  8. package/bin/install.mjs +491 -491
  9. package/bin/migrate-eng-buddy-data.py +875 -0
  10. package/bin/playbook_engine/__init__.py +1 -0
  11. package/bin/playbook_engine/conftest.py +8 -0
  12. package/bin/playbook_engine/extractor.py +33 -0
  13. package/bin/playbook_engine/manager.py +102 -0
  14. package/bin/playbook_engine/models.py +84 -0
  15. package/bin/playbook_engine/registry.py +35 -0
  16. package/bin/playbook_engine/test_extractor.py +72 -0
  17. package/bin/playbook_engine/test_integration.py +129 -0
  18. package/bin/playbook_engine/test_manager.py +85 -0
  19. package/bin/playbook_engine/test_models.py +166 -0
  20. package/bin/playbook_engine/test_registry.py +67 -0
  21. package/bin/playbook_engine/test_tracer.py +86 -0
  22. package/bin/playbook_engine/tracer.py +93 -0
  23. package/bin/tasks_db.py +456 -0
  24. package/docs/HOOKS.md +243 -243
  25. package/docs/INBOX.md +233 -233
  26. package/ftm/SKILL.md +125 -122
  27. package/ftm-audit/SKILL.md +673 -623
  28. package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
  29. package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
  30. package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
  31. package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
  32. package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
  33. package/ftm-audit/scripts/run-knip.sh +23 -23
  34. package/ftm-audit.yml +2 -2
  35. package/ftm-brainstorm/SKILL.md +1003 -498
  36. package/ftm-brainstorm/evals/evals.json +180 -100
  37. package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
  38. package/ftm-brainstorm/references/agent-prompts.md +552 -224
  39. package/ftm-brainstorm/references/plan-template.md +209 -121
  40. package/ftm-brainstorm.yml +2 -2
  41. package/ftm-browse/SKILL.md +454 -454
  42. package/ftm-browse/daemon/browser-manager.ts +206 -206
  43. package/ftm-browse/daemon/bun.lock +30 -30
  44. package/ftm-browse/daemon/cli.ts +347 -347
  45. package/ftm-browse/daemon/commands.ts +410 -410
  46. package/ftm-browse/daemon/main.ts +357 -357
  47. package/ftm-browse/daemon/package.json +17 -17
  48. package/ftm-browse/daemon/server.ts +189 -189
  49. package/ftm-browse/daemon/snapshot.ts +519 -519
  50. package/ftm-browse/daemon/tsconfig.json +22 -22
  51. package/ftm-browse.yml +4 -4
  52. package/ftm-capture/SKILL.md +370 -370
  53. package/ftm-capture.yml +4 -4
  54. package/ftm-codex-gate/SKILL.md +361 -361
  55. package/ftm-codex-gate.yml +2 -2
  56. package/ftm-config/SKILL.md +422 -345
  57. package/ftm-config.default.yml +125 -82
  58. package/ftm-config.yml +44 -2
  59. package/ftm-council/SKILL.md +416 -416
  60. package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
  61. package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
  62. package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
  63. package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
  64. package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
  65. package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
  66. package/ftm-council-chat.yml +2 -0
  67. package/ftm-council.yml +2 -2
  68. package/ftm-dashboard/SKILL.md +163 -163
  69. package/ftm-dashboard.yml +4 -4
  70. package/ftm-debug/SKILL.md +1037 -1037
  71. package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
  72. package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
  73. package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
  74. package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
  75. package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
  76. package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
  77. package/ftm-debug.yml +2 -2
  78. package/ftm-diagram/SKILL.md +277 -277
  79. package/ftm-diagram.yml +2 -2
  80. package/ftm-executor/SKILL.md +777 -777
  81. package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
  82. package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
  83. package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
  84. package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
  85. package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +81 -72
  86. package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
  87. package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
  88. package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
  89. package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -59
  90. package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
  91. package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
  92. package/ftm-executor/runtime/package.json +8 -8
  93. package/ftm-executor.yml +2 -2
  94. package/ftm-git/SKILL.md +441 -441
  95. package/ftm-git/evals/evals.json +26 -26
  96. package/ftm-git/evals/promptfoo.yaml +75 -75
  97. package/ftm-git/hooks/post-commit-experience.sh +92 -92
  98. package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
  99. package/ftm-git/references/protocols/REMEDIATION.md +139 -139
  100. package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
  101. package/ftm-git.yml +2 -2
  102. package/ftm-inbox/backend/__pycache__/main.cpython-314.pyc +0 -0
  103. package/ftm-inbox/backend/adapters/_retry.py +64 -64
  104. package/ftm-inbox/backend/adapters/base.py +230 -230
  105. package/ftm-inbox/backend/adapters/freshservice.py +104 -104
  106. package/ftm-inbox/backend/adapters/gmail.py +125 -125
  107. package/ftm-inbox/backend/adapters/jira.py +136 -136
  108. package/ftm-inbox/backend/adapters/registry.py +192 -192
  109. package/ftm-inbox/backend/adapters/slack.py +110 -110
  110. package/ftm-inbox/backend/db/connection.py +54 -54
  111. package/ftm-inbox/backend/db/schema.py +78 -78
  112. package/ftm-inbox/backend/executor/__init__.py +7 -7
  113. package/ftm-inbox/backend/executor/engine.py +149 -149
  114. package/ftm-inbox/backend/executor/step_runner.py +98 -98
  115. package/ftm-inbox/backend/main.py +103 -103
  116. package/ftm-inbox/backend/models/__init__.py +1 -1
  117. package/ftm-inbox/backend/models/unified_task.py +36 -36
  118. package/ftm-inbox/backend/planner/__init__.py +6 -6
  119. package/ftm-inbox/backend/planner/__pycache__/__init__.cpython-314.pyc +0 -0
  120. package/ftm-inbox/backend/planner/__pycache__/generator.cpython-314.pyc +0 -0
  121. package/ftm-inbox/backend/planner/__pycache__/schema.cpython-314.pyc +0 -0
  122. package/ftm-inbox/backend/planner/generator.py +127 -127
  123. package/ftm-inbox/backend/planner/schema.py +34 -34
  124. package/ftm-inbox/backend/requirements.txt +5 -5
  125. package/ftm-inbox/backend/routes/__pycache__/plan.cpython-314.pyc +0 -0
  126. package/ftm-inbox/backend/routes/execute.py +186 -186
  127. package/ftm-inbox/backend/routes/health.py +52 -52
  128. package/ftm-inbox/backend/routes/inbox.py +68 -68
  129. package/ftm-inbox/backend/routes/plan.py +271 -271
  130. package/ftm-inbox/bin/launchagent.mjs +91 -91
  131. package/ftm-inbox/bin/setup.mjs +188 -188
  132. package/ftm-inbox/bin/start.sh +10 -10
  133. package/ftm-inbox/bin/status.sh +17 -17
  134. package/ftm-inbox/bin/stop.sh +8 -8
  135. package/ftm-inbox/config.example.yml +55 -55
  136. package/ftm-inbox/package-lock.json +2898 -2898
  137. package/ftm-inbox/package.json +26 -26
  138. package/ftm-inbox/postcss.config.js +6 -6
  139. package/ftm-inbox/src/app.css +199 -199
  140. package/ftm-inbox/src/app.html +18 -18
  141. package/ftm-inbox/src/lib/api.ts +166 -166
  142. package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
  143. package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
  144. package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
  145. package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
  146. package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
  147. package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
  148. package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
  149. package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
  150. package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
  151. package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
  152. package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
  153. package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
  154. package/ftm-inbox/src/lib/theme.ts +47 -47
  155. package/ftm-inbox/src/routes/+layout.svelte +76 -76
  156. package/ftm-inbox/src/routes/+page.svelte +401 -401
  157. package/ftm-inbox/svelte.config.js +12 -12
  158. package/ftm-inbox/tailwind.config.ts +63 -63
  159. package/ftm-inbox/tsconfig.json +13 -13
  160. package/ftm-inbox/vite.config.ts +6 -6
  161. package/ftm-intent/SKILL.md +241 -241
  162. package/ftm-intent.yml +2 -2
  163. package/ftm-manifest.json +3794 -3794
  164. package/ftm-map/SKILL.md +291 -291
  165. package/ftm-map/scripts/db.py +712 -712
  166. package/ftm-map/scripts/index.py +415 -415
  167. package/ftm-map/scripts/parser.py +224 -224
  168. package/ftm-map/scripts/queries/go-tags.scm +20 -20
  169. package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
  170. package/ftm-map/scripts/queries/python-tags.scm +31 -31
  171. package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
  172. package/ftm-map/scripts/queries/rust-tags.scm +37 -37
  173. package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
  174. package/ftm-map/scripts/query.py +301 -301
  175. package/ftm-map/scripts/ranker.py +377 -377
  176. package/ftm-map/scripts/requirements.txt +5 -5
  177. package/ftm-map/scripts/setup-hooks.sh +27 -27
  178. package/ftm-map/scripts/setup.sh +56 -56
  179. package/ftm-map/scripts/test_db.py +364 -364
  180. package/ftm-map/scripts/test_parser.py +174 -174
  181. package/ftm-map/scripts/test_query.py +183 -183
  182. package/ftm-map/scripts/test_ranker.py +199 -199
  183. package/ftm-map/scripts/views.py +591 -591
  184. package/ftm-map.yml +2 -2
  185. package/ftm-mind/SKILL.md +201 -1943
  186. package/ftm-mind/evals/promptfoo.yaml +142 -142
  187. package/ftm-mind/references/blackboard-protocol.md +110 -0
  188. package/ftm-mind/references/blackboard-schema.md +328 -328
  189. package/ftm-mind/references/complexity-guide.md +110 -110
  190. package/ftm-mind/references/complexity-sizing.md +138 -0
  191. package/ftm-mind/references/decide-act-protocol.md +172 -0
  192. package/ftm-mind/references/direct-execution.md +51 -0
  193. package/ftm-mind/references/environment-discovery.md +77 -0
  194. package/ftm-mind/references/event-registry.md +319 -319
  195. package/ftm-mind/references/mcp-inventory.md +300 -296
  196. package/ftm-mind/references/ops-routing.md +47 -0
  197. package/ftm-mind/references/orient-protocol.md +234 -0
  198. package/ftm-mind/references/personality.md +40 -0
  199. package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
  200. package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
  201. package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
  202. package/ftm-mind/references/reflexion-protocol.md +249 -249
  203. package/ftm-mind/references/routing/SCENARIOS.md +22 -22
  204. package/ftm-mind/references/routing-scenarios.md +35 -35
  205. package/ftm-mind.yml +2 -2
  206. package/ftm-ops.yml +4 -0
  207. package/ftm-pause/SKILL.md +395 -395
  208. package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
  209. package/ftm-pause/references/protocols/VALIDATION.md +80 -80
  210. package/ftm-pause.yml +2 -2
  211. package/ftm-researcher/SKILL.md +275 -275
  212. package/ftm-researcher/evals/agent-diversity.yaml +17 -17
  213. package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
  214. package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
  215. package/ftm-researcher/references/adaptive-search.md +116 -116
  216. package/ftm-researcher/references/agent-prompts.md +193 -193
  217. package/ftm-researcher/references/council-integration.md +193 -193
  218. package/ftm-researcher/references/output-format.md +203 -203
  219. package/ftm-researcher/references/synthesis-pipeline.md +165 -165
  220. package/ftm-researcher/scripts/score_credibility.py +234 -234
  221. package/ftm-researcher/scripts/validate_research.py +92 -92
  222. package/ftm-researcher.yml +2 -2
  223. package/ftm-resume/SKILL.md +518 -518
  224. package/ftm-resume/references/protocols/VALIDATION.md +172 -172
  225. package/ftm-resume.yml +2 -2
  226. package/ftm-retro/SKILL.md +380 -380
  227. package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
  228. package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
  229. package/ftm-retro.yml +2 -2
  230. package/ftm-routine/SKILL.md +170 -170
  231. package/ftm-routine.yml +4 -4
  232. package/ftm-state/blackboard/capabilities.json +5 -5
  233. package/ftm-state/blackboard/capabilities.schema.json +27 -27
  234. package/ftm-state/blackboard/context.json +37 -23
  235. package/ftm-state/blackboard/experiences/doom-statusline-fix.json +26 -0
  236. package/ftm-state/blackboard/experiences/hackathon-pages-site.json +26 -0
  237. package/ftm-state/blackboard/experiences/hindsight-sso-kickoff.json +42 -0
  238. package/ftm-state/blackboard/experiences/index.json +58 -9
  239. package/ftm-state/blackboard/experiences/learning-ragnarok-api-access.json +23 -0
  240. package/ftm-state/blackboard/experiences/nordlayer-members-auto-assign.json +26 -0
  241. package/ftm-state/blackboard/experiences/saml2aws-stale-session-fix.json +41 -0
  242. package/ftm-state/blackboard/patterns.json +6 -6
  243. package/ftm-state/schemas/context.schema.json +130 -130
  244. package/ftm-state/schemas/experience-index.schema.json +77 -77
  245. package/ftm-state/schemas/experience.schema.json +78 -78
  246. package/ftm-state/schemas/patterns.schema.json +44 -44
  247. package/ftm-upgrade/SKILL.md +194 -194
  248. package/ftm-upgrade/scripts/check-version.sh +76 -76
  249. package/ftm-upgrade/scripts/upgrade.sh +143 -143
  250. package/ftm-upgrade.yml +2 -2
  251. package/ftm-verify.yml +2 -2
  252. package/ftm.yml +2 -2
  253. package/hooks/ftm-auto-log.sh +137 -0
  254. package/hooks/ftm-blackboard-enforcer.sh +93 -93
  255. package/hooks/ftm-discovery-reminder.sh +90 -90
  256. package/hooks/ftm-drafts-gate.sh +61 -61
  257. package/hooks/ftm-event-logger.mjs +107 -107
  258. package/hooks/ftm-install-hooks.sh +240 -0
  259. package/hooks/ftm-learning-capture.sh +117 -0
  260. package/hooks/ftm-map-autodetect.sh +79 -79
  261. package/hooks/ftm-pending-sync-check.sh +22 -22
  262. package/hooks/ftm-plan-gate.sh +92 -92
  263. package/hooks/ftm-post-commit-trigger.sh +57 -57
  264. package/hooks/ftm-post-compaction.sh +138 -0
  265. package/hooks/ftm-pre-compaction.sh +147 -0
  266. package/hooks/ftm-session-end.sh +52 -0
  267. package/hooks/ftm-session-snapshot.sh +213 -0
  268. package/hooks/ftm-task-loader.sh +100 -0
  269. package/hooks/settings-template.json +91 -81
  270. package/install.sh +363 -363
  271. package/package.json +84 -84
  272. package/uninstall.sh +25 -25
@@ -1,91 +1,91 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Generates ~/Library/LaunchAgents/com.ftm.inbox.plist
5
- * Runs ftm-inbox on login (macOS only).
6
- */
7
-
8
- import { existsSync, mkdirSync, writeFileSync } from "fs";
9
- import { join } from "path";
10
- import { homedir, platform } from "os";
11
- import { execSync } from "child_process";
12
- import { fileURLToPath } from "url";
13
- import { dirname } from "path";
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = dirname(__filename);
17
-
18
- const HOME = homedir();
19
- const LAUNCH_AGENTS_DIR = join(HOME, "Library", "LaunchAgents");
20
- const PLIST_PATH = join(LAUNCH_AGENTS_DIR, "com.ftm.inbox.plist");
21
- const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
22
- const START_SCRIPT = join(__dirname, "start.sh");
23
-
24
- function ensureDir(dir) {
25
- if (!existsSync(dir)) {
26
- mkdirSync(dir, { recursive: true });
27
- }
28
- }
29
-
30
- function buildPlist(startScript, logDir) {
31
- return `<?xml version="1.0" encoding="UTF-8"?>
32
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
- <plist version="1.0">
34
- <dict>
35
- <key>Label</key>
36
- <string>com.ftm.inbox</string>
37
-
38
- <key>ProgramArguments</key>
39
- <array>
40
- <string>/bin/bash</string>
41
- <string>${startScript}</string>
42
- </array>
43
-
44
- <key>RunAtLoad</key>
45
- <true/>
46
-
47
- <key>KeepAlive</key>
48
- <false/>
49
-
50
- <key>StandardOutPath</key>
51
- <string>${join(logDir, "launchd-stdout.log")}</string>
52
-
53
- <key>StandardErrorPath</key>
54
- <string>${join(logDir, "launchd-stderr.log")}</string>
55
-
56
- <key>WorkingDirectory</key>
57
- <string>${dirname(dirname(startScript))}</string>
58
- </dict>
59
- </plist>
60
- `;
61
- }
62
-
63
- function main() {
64
- if (platform() !== "darwin") {
65
- console.error("LaunchAgent setup is macOS-only.");
66
- process.exit(1);
67
- }
68
-
69
- const logDir = join(INBOX_CONFIG_DIR, "logs");
70
-
71
- ensureDir(LAUNCH_AGENTS_DIR);
72
- ensureDir(logDir);
73
-
74
- const plistContent = buildPlist(START_SCRIPT, logDir);
75
- writeFileSync(PLIST_PATH, plistContent, "utf8");
76
-
77
- console.log("LaunchAgent written to:", PLIST_PATH);
78
- console.log("Logs will be written to:", logDir);
79
-
80
- // Load it immediately
81
- try {
82
- execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
83
- console.log("LaunchAgent loaded. ftm-inbox will start on next login.");
84
- console.log("To start it now: launchctl start com.ftm.inbox");
85
- } catch (err) {
86
- console.warn("Could not load LaunchAgent automatically:", err.message);
87
- console.warn("Run manually: launchctl load", PLIST_PATH);
88
- }
89
- }
90
-
91
- main();
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Generates ~/Library/LaunchAgents/com.ftm.inbox.plist
5
+ * Runs ftm-inbox on login (macOS only).
6
+ */
7
+
8
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
9
+ import { join } from "path";
10
+ import { homedir, platform } from "os";
11
+ import { execSync } from "child_process";
12
+ import { fileURLToPath } from "url";
13
+ import { dirname } from "path";
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ const HOME = homedir();
19
+ const LAUNCH_AGENTS_DIR = join(HOME, "Library", "LaunchAgents");
20
+ const PLIST_PATH = join(LAUNCH_AGENTS_DIR, "com.ftm.inbox.plist");
21
+ const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
22
+ const START_SCRIPT = join(__dirname, "start.sh");
23
+
24
+ function ensureDir(dir) {
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ }
29
+
30
+ function buildPlist(startScript, logDir) {
31
+ return `<?xml version="1.0" encoding="UTF-8"?>
32
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
+ <plist version="1.0">
34
+ <dict>
35
+ <key>Label</key>
36
+ <string>com.ftm.inbox</string>
37
+
38
+ <key>ProgramArguments</key>
39
+ <array>
40
+ <string>/bin/bash</string>
41
+ <string>${startScript}</string>
42
+ </array>
43
+
44
+ <key>RunAtLoad</key>
45
+ <true/>
46
+
47
+ <key>KeepAlive</key>
48
+ <false/>
49
+
50
+ <key>StandardOutPath</key>
51
+ <string>${join(logDir, "launchd-stdout.log")}</string>
52
+
53
+ <key>StandardErrorPath</key>
54
+ <string>${join(logDir, "launchd-stderr.log")}</string>
55
+
56
+ <key>WorkingDirectory</key>
57
+ <string>${dirname(dirname(startScript))}</string>
58
+ </dict>
59
+ </plist>
60
+ `;
61
+ }
62
+
63
+ function main() {
64
+ if (platform() !== "darwin") {
65
+ console.error("LaunchAgent setup is macOS-only.");
66
+ process.exit(1);
67
+ }
68
+
69
+ const logDir = join(INBOX_CONFIG_DIR, "logs");
70
+
71
+ ensureDir(LAUNCH_AGENTS_DIR);
72
+ ensureDir(logDir);
73
+
74
+ const plistContent = buildPlist(START_SCRIPT, logDir);
75
+ writeFileSync(PLIST_PATH, plistContent, "utf8");
76
+
77
+ console.log("LaunchAgent written to:", PLIST_PATH);
78
+ console.log("Logs will be written to:", logDir);
79
+
80
+ // Load it immediately
81
+ try {
82
+ execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
83
+ console.log("LaunchAgent loaded. ftm-inbox will start on next login.");
84
+ console.log("To start it now: launchctl start com.ftm.inbox");
85
+ } catch (err) {
86
+ console.warn("Could not load LaunchAgent automatically:", err.message);
87
+ console.warn("Run manually: launchctl load", PLIST_PATH);
88
+ }
89
+ }
90
+
91
+ main();
@@ -1,188 +1,188 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * ftm-inbox setup wizard
5
- * Prompts for credentials and writes ~/.claude/ftm-inbox/config.yml
6
- */
7
-
8
- import { createInterface } from "readline";
9
- import { existsSync, mkdirSync, writeFileSync } from "fs";
10
- import { join } from "path";
11
- import { homedir } from "os";
12
-
13
- const HOME = homedir();
14
- const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
15
- const CONFIG_FILE = join(INBOX_CONFIG_DIR, "config.yml");
16
-
17
- const rl = createInterface({
18
- input: process.stdin,
19
- output: process.stdout,
20
- });
21
-
22
- function ask(question, defaultValue = "") {
23
- return new Promise((resolve) => {
24
- const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
25
- rl.question(prompt, (answer) => {
26
- resolve(answer.trim() || defaultValue);
27
- });
28
- });
29
- }
30
-
31
- function askSecret(question) {
32
- return new Promise((resolve) => {
33
- process.stdout.write(`${question}: `);
34
- // Note: readline doesn't natively hide input; use as-is for terminal environments
35
- rl.question("", (answer) => {
36
- resolve(answer.trim());
37
- });
38
- });
39
- }
40
-
41
- function ensureDir(dir) {
42
- if (!existsSync(dir)) {
43
- mkdirSync(dir, { recursive: true });
44
- }
45
- }
46
-
47
- function buildConfig(answers) {
48
- const lines = ["# ftm-inbox configuration", "# Generated by ftm-inbox setup wizard", ""];
49
-
50
- lines.push("server:");
51
- lines.push(` port: ${answers.port}`);
52
- lines.push("");
53
-
54
- if (answers.jiraBaseUrl || answers.jiraEmail || answers.jiraApiToken) {
55
- lines.push("adapters:");
56
- lines.push(" jira:");
57
- lines.push(` enabled: ${!!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken)}`);
58
- lines.push(` base_url: "${answers.jiraBaseUrl}"`);
59
- lines.push(` email: "${answers.jiraEmail}"`);
60
- lines.push(` api_token: "${answers.jiraApiToken}"`);
61
- lines.push(` poll_interval_seconds: 60`);
62
- lines.push("");
63
- } else {
64
- lines.push("adapters:");
65
- lines.push(" jira:");
66
- lines.push(" enabled: false");
67
- lines.push(` base_url: ""`);
68
- lines.push(` email: ""`);
69
- lines.push(` api_token: ""`);
70
- lines.push(` poll_interval_seconds: 60`);
71
- lines.push("");
72
- }
73
-
74
- lines.push(" freshservice:");
75
- lines.push(` enabled: ${!!(answers.freshserviceDomain && answers.freshserviceApiKey)}`);
76
- lines.push(` domain: "${answers.freshserviceDomain}"`);
77
- lines.push(` api_key: "${answers.freshserviceApiKey}"`);
78
- lines.push(` poll_interval_seconds: 120`);
79
- lines.push("");
80
-
81
- lines.push(" slack:");
82
- lines.push(` enabled: ${!!answers.slackBotToken}`);
83
- lines.push(` bot_token: "${answers.slackBotToken}"`);
84
- lines.push(` poll_interval_seconds: 30`);
85
- lines.push("");
86
-
87
- lines.push(" gmail:");
88
- lines.push(` enabled: ${!!answers.gmailCredentialsPath}`);
89
- lines.push(` credentials_path: "${answers.gmailCredentialsPath}"`);
90
- lines.push(` poll_interval_seconds: 120`);
91
- lines.push("");
92
-
93
- lines.push("database:");
94
- lines.push(` path: "${join(INBOX_CONFIG_DIR, "inbox.db")}"`);
95
- lines.push("");
96
-
97
- lines.push("logging:");
98
- lines.push(` level: "INFO"`);
99
- lines.push(` path: "${join(INBOX_CONFIG_DIR, "logs")}"`);
100
-
101
- return lines.join("\n");
102
- }
103
-
104
- async function main() {
105
- console.log("");
106
- console.log("ftm-inbox Setup Wizard");
107
- console.log("======================");
108
- console.log("This will configure your inbox service credentials.");
109
- console.log("Credentials are stored in ~/.claude/ftm-inbox/config.yml (gitignored).");
110
- console.log("");
111
-
112
- const answers = {};
113
-
114
- // Port
115
- answers.port = await ask("Port number", "8042");
116
-
117
- // Jira
118
- console.log("");
119
- console.log("--- Jira (leave blank to skip) ---");
120
- answers.jiraBaseUrl = await ask("Jira base URL (e.g. https://yourorg.atlassian.net)");
121
- if (answers.jiraBaseUrl) {
122
- answers.jiraEmail = await ask("Jira email");
123
- answers.jiraApiToken = await askSecret("Jira API token");
124
- } else {
125
- answers.jiraEmail = "";
126
- answers.jiraApiToken = "";
127
- }
128
-
129
- // Freshservice
130
- console.log("");
131
- console.log("--- Freshservice (leave blank to skip) ---");
132
- answers.freshserviceDomain = await ask("Freshservice domain (e.g. yourorg.freshservice.com)");
133
- if (answers.freshserviceDomain) {
134
- answers.freshserviceApiKey = await askSecret("Freshservice API key");
135
- } else {
136
- answers.freshserviceApiKey = "";
137
- }
138
-
139
- // Slack
140
- console.log("");
141
- console.log("--- Slack (leave blank to skip) ---");
142
- answers.slackBotToken = await askSecret("Slack bot token (xoxb-...)");
143
-
144
- // Gmail
145
- console.log("");
146
- console.log("--- Gmail (leave blank to skip) ---");
147
- answers.gmailCredentialsPath = await ask(
148
- "Gmail credentials JSON path (e.g. ~/credentials.json)"
149
- );
150
-
151
- rl.close();
152
-
153
- // Validate at least one adapter
154
- const hasJira = !!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken);
155
- const hasFreshservice = !!(answers.freshserviceDomain && answers.freshserviceApiKey);
156
- const hasSlack = !!answers.slackBotToken;
157
- const hasGmail = !!answers.gmailCredentialsPath;
158
-
159
- if (!hasJira && !hasFreshservice && !hasSlack && !hasGmail) {
160
- console.log("");
161
- console.error("ERROR: At least one adapter must have credentials configured.");
162
- console.error("Re-run setup and provide credentials for at least one service.");
163
- process.exit(1);
164
- }
165
-
166
- // Write config
167
- ensureDir(INBOX_CONFIG_DIR);
168
- ensureDir(join(INBOX_CONFIG_DIR, "logs"));
169
-
170
- const configContent = buildConfig(answers);
171
- writeFileSync(CONFIG_FILE, configContent, "utf8");
172
-
173
- console.log("");
174
- console.log("Configuration written to:", CONFIG_FILE);
175
- console.log("");
176
- console.log("Enabled adapters:");
177
- if (hasJira) console.log(" - Jira");
178
- if (hasFreshservice) console.log(" - Freshservice");
179
- if (hasSlack) console.log(" - Slack");
180
- if (hasGmail) console.log(" - Gmail");
181
- console.log("");
182
- console.log("Start the service with: ftm-inbox/bin/start.sh");
183
- }
184
-
185
- main().catch((err) => {
186
- console.error("Setup failed:", err.message);
187
- process.exit(1);
188
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ftm-inbox setup wizard
5
+ * Prompts for credentials and writes ~/.claude/ftm-inbox/config.yml
6
+ */
7
+
8
+ import { createInterface } from "readline";
9
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
10
+ import { join } from "path";
11
+ import { homedir } from "os";
12
+
13
+ const HOME = homedir();
14
+ const INBOX_CONFIG_DIR = join(HOME, ".claude", "ftm-inbox");
15
+ const CONFIG_FILE = join(INBOX_CONFIG_DIR, "config.yml");
16
+
17
+ const rl = createInterface({
18
+ input: process.stdin,
19
+ output: process.stdout,
20
+ });
21
+
22
+ function ask(question, defaultValue = "") {
23
+ return new Promise((resolve) => {
24
+ const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
25
+ rl.question(prompt, (answer) => {
26
+ resolve(answer.trim() || defaultValue);
27
+ });
28
+ });
29
+ }
30
+
31
+ function askSecret(question) {
32
+ return new Promise((resolve) => {
33
+ process.stdout.write(`${question}: `);
34
+ // Note: readline doesn't natively hide input; use as-is for terminal environments
35
+ rl.question("", (answer) => {
36
+ resolve(answer.trim());
37
+ });
38
+ });
39
+ }
40
+
41
+ function ensureDir(dir) {
42
+ if (!existsSync(dir)) {
43
+ mkdirSync(dir, { recursive: true });
44
+ }
45
+ }
46
+
47
+ function buildConfig(answers) {
48
+ const lines = ["# ftm-inbox configuration", "# Generated by ftm-inbox setup wizard", ""];
49
+
50
+ lines.push("server:");
51
+ lines.push(` port: ${answers.port}`);
52
+ lines.push("");
53
+
54
+ if (answers.jiraBaseUrl || answers.jiraEmail || answers.jiraApiToken) {
55
+ lines.push("adapters:");
56
+ lines.push(" jira:");
57
+ lines.push(` enabled: ${!!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken)}`);
58
+ lines.push(` base_url: "${answers.jiraBaseUrl}"`);
59
+ lines.push(` email: "${answers.jiraEmail}"`);
60
+ lines.push(` api_token: "${answers.jiraApiToken}"`);
61
+ lines.push(` poll_interval_seconds: 60`);
62
+ lines.push("");
63
+ } else {
64
+ lines.push("adapters:");
65
+ lines.push(" jira:");
66
+ lines.push(" enabled: false");
67
+ lines.push(` base_url: ""`);
68
+ lines.push(` email: ""`);
69
+ lines.push(` api_token: ""`);
70
+ lines.push(` poll_interval_seconds: 60`);
71
+ lines.push("");
72
+ }
73
+
74
+ lines.push(" freshservice:");
75
+ lines.push(` enabled: ${!!(answers.freshserviceDomain && answers.freshserviceApiKey)}`);
76
+ lines.push(` domain: "${answers.freshserviceDomain}"`);
77
+ lines.push(` api_key: "${answers.freshserviceApiKey}"`);
78
+ lines.push(` poll_interval_seconds: 120`);
79
+ lines.push("");
80
+
81
+ lines.push(" slack:");
82
+ lines.push(` enabled: ${!!answers.slackBotToken}`);
83
+ lines.push(` bot_token: "${answers.slackBotToken}"`);
84
+ lines.push(` poll_interval_seconds: 30`);
85
+ lines.push("");
86
+
87
+ lines.push(" gmail:");
88
+ lines.push(` enabled: ${!!answers.gmailCredentialsPath}`);
89
+ lines.push(` credentials_path: "${answers.gmailCredentialsPath}"`);
90
+ lines.push(` poll_interval_seconds: 120`);
91
+ lines.push("");
92
+
93
+ lines.push("database:");
94
+ lines.push(` path: "${join(INBOX_CONFIG_DIR, "inbox.db")}"`);
95
+ lines.push("");
96
+
97
+ lines.push("logging:");
98
+ lines.push(` level: "INFO"`);
99
+ lines.push(` path: "${join(INBOX_CONFIG_DIR, "logs")}"`);
100
+
101
+ return lines.join("\n");
102
+ }
103
+
104
+ async function main() {
105
+ console.log("");
106
+ console.log("ftm-inbox Setup Wizard");
107
+ console.log("======================");
108
+ console.log("This will configure your inbox service credentials.");
109
+ console.log("Credentials are stored in ~/.claude/ftm-inbox/config.yml (gitignored).");
110
+ console.log("");
111
+
112
+ const answers = {};
113
+
114
+ // Port
115
+ answers.port = await ask("Port number", "8042");
116
+
117
+ // Jira
118
+ console.log("");
119
+ console.log("--- Jira (leave blank to skip) ---");
120
+ answers.jiraBaseUrl = await ask("Jira base URL (e.g. https://yourorg.atlassian.net)");
121
+ if (answers.jiraBaseUrl) {
122
+ answers.jiraEmail = await ask("Jira email");
123
+ answers.jiraApiToken = await askSecret("Jira API token");
124
+ } else {
125
+ answers.jiraEmail = "";
126
+ answers.jiraApiToken = "";
127
+ }
128
+
129
+ // Freshservice
130
+ console.log("");
131
+ console.log("--- Freshservice (leave blank to skip) ---");
132
+ answers.freshserviceDomain = await ask("Freshservice domain (e.g. yourorg.freshservice.com)");
133
+ if (answers.freshserviceDomain) {
134
+ answers.freshserviceApiKey = await askSecret("Freshservice API key");
135
+ } else {
136
+ answers.freshserviceApiKey = "";
137
+ }
138
+
139
+ // Slack
140
+ console.log("");
141
+ console.log("--- Slack (leave blank to skip) ---");
142
+ answers.slackBotToken = await askSecret("Slack bot token (xoxb-...)");
143
+
144
+ // Gmail
145
+ console.log("");
146
+ console.log("--- Gmail (leave blank to skip) ---");
147
+ answers.gmailCredentialsPath = await ask(
148
+ "Gmail credentials JSON path (e.g. ~/credentials.json)"
149
+ );
150
+
151
+ rl.close();
152
+
153
+ // Validate at least one adapter
154
+ const hasJira = !!(answers.jiraBaseUrl && answers.jiraEmail && answers.jiraApiToken);
155
+ const hasFreshservice = !!(answers.freshserviceDomain && answers.freshserviceApiKey);
156
+ const hasSlack = !!answers.slackBotToken;
157
+ const hasGmail = !!answers.gmailCredentialsPath;
158
+
159
+ if (!hasJira && !hasFreshservice && !hasSlack && !hasGmail) {
160
+ console.log("");
161
+ console.error("ERROR: At least one adapter must have credentials configured.");
162
+ console.error("Re-run setup and provide credentials for at least one service.");
163
+ process.exit(1);
164
+ }
165
+
166
+ // Write config
167
+ ensureDir(INBOX_CONFIG_DIR);
168
+ ensureDir(join(INBOX_CONFIG_DIR, "logs"));
169
+
170
+ const configContent = buildConfig(answers);
171
+ writeFileSync(CONFIG_FILE, configContent, "utf8");
172
+
173
+ console.log("");
174
+ console.log("Configuration written to:", CONFIG_FILE);
175
+ console.log("");
176
+ console.log("Enabled adapters:");
177
+ if (hasJira) console.log(" - Jira");
178
+ if (hasFreshservice) console.log(" - Freshservice");
179
+ if (hasSlack) console.log(" - Slack");
180
+ if (hasGmail) console.log(" - Gmail");
181
+ console.log("");
182
+ console.log("Start the service with: ftm-inbox/bin/start.sh");
183
+ }
184
+
185
+ main().catch((err) => {
186
+ console.error("Setup failed:", err.message);
187
+ process.exit(1);
188
+ });
@@ -1,10 +1,10 @@
1
- #!/bin/bash
2
- # Start ftm-inbox backend + pollers
3
- cd "$(dirname "$0")/.." || exit
4
- PORT=${FTM_INBOX_PORT:-8042}
5
- echo "Starting ftm-inbox on port $PORT..."
6
- python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT &
7
- BACKEND_PID=$!
8
- echo "Backend PID: $BACKEND_PID"
9
- echo $BACKEND_PID > /tmp/ftm-inbox.pid
10
- echo "ftm-inbox running. Stop with: ftm-inbox/bin/stop.sh"
1
+ #!/bin/bash
2
+ # Start ftm-inbox backend + pollers
3
+ cd "$(dirname "$0")/.." || exit
4
+ PORT=${FTM_INBOX_PORT:-8042}
5
+ echo "Starting ftm-inbox on port $PORT..."
6
+ python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT &
7
+ BACKEND_PID=$!
8
+ echo "Backend PID: $BACKEND_PID"
9
+ echo $BACKEND_PID > /tmp/ftm-inbox.pid
10
+ echo "ftm-inbox running. Stop with: ftm-inbox/bin/stop.sh"
@@ -1,17 +1,17 @@
1
- #!/bin/bash
2
- if [ -f /tmp/ftm-inbox.pid ] && kill -0 "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null; then
3
- echo "ftm-inbox is running (PID: $(cat /tmp/ftm-inbox.pid))"
4
- # Show last poll times from DB if available
5
- CONFIG_DIR="$HOME/.claude/ftm-inbox"
6
- DB_PATH="$CONFIG_DIR/inbox.db"
7
- if [ -f "$DB_PATH" ] && command -v sqlite3 &>/dev/null; then
8
- echo ""
9
- echo "Last poll times:"
10
- sqlite3 "$DB_PATH" "SELECT adapter, MAX(fetched_at) FROM inbox_items GROUP BY adapter;" 2>/dev/null | \
11
- while IFS='|' read -r adapter ts; do
12
- echo " $adapter: $ts"
13
- done
14
- fi
15
- else
16
- echo "ftm-inbox is not running."
17
- fi
1
+ #!/bin/bash
2
+ if [ -f /tmp/ftm-inbox.pid ] && kill -0 "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null; then
3
+ echo "ftm-inbox is running (PID: $(cat /tmp/ftm-inbox.pid))"
4
+ # Show last poll times from DB if available
5
+ CONFIG_DIR="$HOME/.claude/ftm-inbox"
6
+ DB_PATH="$CONFIG_DIR/inbox.db"
7
+ if [ -f "$DB_PATH" ] && command -v sqlite3 &>/dev/null; then
8
+ echo ""
9
+ echo "Last poll times:"
10
+ sqlite3 "$DB_PATH" "SELECT adapter, MAX(fetched_at) FROM inbox_items GROUP BY adapter;" 2>/dev/null | \
11
+ while IFS='|' read -r adapter ts; do
12
+ echo " $adapter: $ts"
13
+ done
14
+ fi
15
+ else
16
+ echo "ftm-inbox is not running."
17
+ fi
@@ -1,8 +1,8 @@
1
- #!/bin/bash
2
- if [ -f /tmp/ftm-inbox.pid ]; then
3
- kill "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null
4
- rm /tmp/ftm-inbox.pid
5
- echo "ftm-inbox stopped."
6
- else
7
- echo "No running ftm-inbox found."
8
- fi
1
+ #!/bin/bash
2
+ if [ -f /tmp/ftm-inbox.pid ]; then
3
+ kill "$(cat /tmp/ftm-inbox.pid)" 2>/dev/null
4
+ rm /tmp/ftm-inbox.pid
5
+ echo "ftm-inbox stopped."
6
+ else
7
+ echo "No running ftm-inbox found."
8
+ fi