feed-the-machine 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +170 -170
- package/bin/generate-manifest.mjs +463 -463
- package/bin/install.mjs +491 -491
- package/docs/HOOKS.md +243 -243
- package/docs/INBOX.md +233 -233
- package/ftm/SKILL.md +122 -122
- package/ftm-audit/SKILL.md +623 -541
- package/ftm-audit/references/protocols/PROJECT-PATTERNS.md +91 -91
- package/ftm-audit/references/protocols/RUNTIME-WIRING.md +66 -66
- package/ftm-audit/references/protocols/WIRING-CONTRACTS.md +135 -135
- package/ftm-audit/references/strategies/AUTO-FIX-STRATEGIES.md +69 -69
- package/ftm-audit/references/templates/REPORT-FORMAT.md +96 -96
- package/ftm-audit/scripts/run-knip.sh +23 -23
- package/ftm-audit.yml +2 -2
- package/ftm-brainstorm/SKILL.md +498 -498
- package/ftm-brainstorm/evals/evals.json +100 -100
- package/ftm-brainstorm/evals/promptfoo.yaml +109 -109
- package/ftm-brainstorm/references/agent-prompts.md +224 -224
- package/ftm-brainstorm/references/plan-template.md +121 -121
- package/ftm-brainstorm.yml +2 -2
- package/ftm-browse/SKILL.md +454 -454
- package/ftm-browse/daemon/browser-manager.ts +206 -206
- package/ftm-browse/daemon/bun.lock +30 -30
- package/ftm-browse/daemon/cli.ts +347 -347
- package/ftm-browse/daemon/commands.ts +410 -410
- package/ftm-browse/daemon/main.ts +357 -357
- package/ftm-browse/daemon/package.json +17 -17
- package/ftm-browse/daemon/server.ts +189 -189
- package/ftm-browse/daemon/snapshot.ts +519 -519
- package/ftm-browse/daemon/tsconfig.json +22 -22
- package/ftm-browse.yml +4 -4
- package/ftm-capture/SKILL.md +370 -370
- package/ftm-capture.yml +4 -4
- package/ftm-codex-gate/SKILL.md +361 -361
- package/ftm-codex-gate.yml +2 -2
- package/ftm-config/SKILL.md +345 -345
- package/ftm-config.default.yml +82 -80
- package/ftm-config.yml +2 -2
- package/ftm-council/SKILL.md +416 -416
- package/ftm-council/references/prompts/CLAUDE-INVESTIGATION.md +60 -60
- package/ftm-council/references/prompts/CODEX-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/GEMINI-INVESTIGATION.md +58 -58
- package/ftm-council/references/prompts/REBUTTAL-TEMPLATE.md +57 -57
- package/ftm-council/references/protocols/PREREQUISITES.md +47 -47
- package/ftm-council/references/protocols/STEP-0-FRAMING.md +46 -46
- package/ftm-council.yml +2 -2
- package/ftm-dashboard/SKILL.md +163 -163
- package/ftm-dashboard.yml +4 -4
- package/ftm-debug/SKILL.md +1037 -1037
- package/ftm-debug/references/phases/PHASE-0-INTAKE.md +58 -58
- package/ftm-debug/references/phases/PHASE-1-TRIAGE.md +46 -46
- package/ftm-debug/references/phases/PHASE-2-WAR-ROOM-AGENTS.md +279 -279
- package/ftm-debug/references/phases/PHASE-3-TO-6-EXECUTION.md +436 -436
- package/ftm-debug/references/protocols/BLACKBOARD.md +86 -86
- package/ftm-debug/references/protocols/EDGE-CASES.md +103 -103
- package/ftm-debug.yml +2 -2
- package/ftm-diagram/SKILL.md +277 -277
- package/ftm-diagram.yml +2 -2
- package/ftm-executor/SKILL.md +777 -767
- package/ftm-executor/references/STYLE-TEMPLATE.md +73 -73
- package/ftm-executor/references/phases/PHASE-0-VERIFICATION.md +62 -62
- package/ftm-executor/references/phases/PHASE-2-AGENT-ASSEMBLY.md +34 -34
- package/ftm-executor/references/phases/PHASE-3-WORKTREES.md +38 -38
- package/ftm-executor/references/phases/PHASE-4-5-AUDIT.md +72 -72
- package/ftm-executor/references/phases/PHASE-4-DISPATCH.md +66 -66
- package/ftm-executor/references/phases/PHASE-5-5-CODEX-GATE.md +73 -73
- package/ftm-executor/references/protocols/DOCUMENTATION-BOOTSTRAP.md +36 -36
- package/ftm-executor/references/protocols/MODEL-PROFILE.md +59 -44
- package/ftm-executor/references/protocols/PROGRESS-TRACKING.md +66 -66
- package/ftm-executor/runtime/ftm-runtime.mjs +252 -252
- package/ftm-executor/runtime/package.json +8 -8
- package/ftm-executor.yml +2 -2
- package/ftm-git/SKILL.md +441 -441
- package/ftm-git/evals/evals.json +26 -26
- package/ftm-git/evals/promptfoo.yaml +75 -75
- package/ftm-git/hooks/post-commit-experience.sh +92 -92
- package/ftm-git/references/patterns/SECRET-PATTERNS.md +104 -104
- package/ftm-git/references/protocols/REMEDIATION.md +139 -139
- package/ftm-git/scripts/pre-commit-secrets.sh +110 -110
- package/ftm-git.yml +2 -2
- package/ftm-inbox/backend/adapters/_retry.py +64 -64
- package/ftm-inbox/backend/adapters/base.py +230 -230
- package/ftm-inbox/backend/adapters/freshservice.py +104 -104
- package/ftm-inbox/backend/adapters/gmail.py +125 -125
- package/ftm-inbox/backend/adapters/jira.py +136 -136
- package/ftm-inbox/backend/adapters/registry.py +192 -192
- package/ftm-inbox/backend/adapters/slack.py +110 -110
- package/ftm-inbox/backend/db/connection.py +54 -54
- package/ftm-inbox/backend/db/schema.py +78 -78
- package/ftm-inbox/backend/executor/__init__.py +7 -7
- package/ftm-inbox/backend/executor/engine.py +149 -149
- package/ftm-inbox/backend/executor/step_runner.py +98 -98
- package/ftm-inbox/backend/main.py +103 -103
- package/ftm-inbox/backend/models/__init__.py +1 -1
- package/ftm-inbox/backend/models/unified_task.py +36 -36
- package/ftm-inbox/backend/planner/__init__.py +6 -6
- package/ftm-inbox/backend/planner/generator.py +127 -127
- package/ftm-inbox/backend/planner/schema.py +34 -34
- package/ftm-inbox/backend/requirements.txt +5 -5
- package/ftm-inbox/backend/routes/execute.py +186 -186
- package/ftm-inbox/backend/routes/health.py +52 -52
- package/ftm-inbox/backend/routes/inbox.py +68 -68
- package/ftm-inbox/backend/routes/plan.py +271 -271
- package/ftm-inbox/bin/launchagent.mjs +91 -91
- package/ftm-inbox/bin/setup.mjs +188 -188
- package/ftm-inbox/bin/start.sh +10 -10
- package/ftm-inbox/bin/status.sh +17 -17
- package/ftm-inbox/bin/stop.sh +8 -8
- package/ftm-inbox/config.example.yml +55 -55
- package/ftm-inbox/package-lock.json +2898 -2898
- package/ftm-inbox/package.json +26 -26
- package/ftm-inbox/postcss.config.js +6 -6
- package/ftm-inbox/src/app.css +199 -199
- package/ftm-inbox/src/app.html +18 -18
- package/ftm-inbox/src/lib/api.ts +166 -166
- package/ftm-inbox/src/lib/components/ExecutionLog.svelte +81 -81
- package/ftm-inbox/src/lib/components/InboxFeed.svelte +143 -143
- package/ftm-inbox/src/lib/components/PlanStep.svelte +271 -271
- package/ftm-inbox/src/lib/components/PlanView.svelte +206 -206
- package/ftm-inbox/src/lib/components/StreamPanel.svelte +99 -99
- package/ftm-inbox/src/lib/components/TaskCard.svelte +190 -190
- package/ftm-inbox/src/lib/components/ui/EmptyState.svelte +63 -63
- package/ftm-inbox/src/lib/components/ui/KawaiiCard.svelte +86 -86
- package/ftm-inbox/src/lib/components/ui/PillButton.svelte +106 -106
- package/ftm-inbox/src/lib/components/ui/StatusBadge.svelte +67 -67
- package/ftm-inbox/src/lib/components/ui/StreamDrawer.svelte +149 -149
- package/ftm-inbox/src/lib/components/ui/ThemeToggle.svelte +80 -80
- package/ftm-inbox/src/lib/theme.ts +47 -47
- package/ftm-inbox/src/routes/+layout.svelte +76 -76
- package/ftm-inbox/src/routes/+page.svelte +401 -401
- package/ftm-inbox/svelte.config.js +12 -12
- package/ftm-inbox/tailwind.config.ts +63 -63
- package/ftm-inbox/tsconfig.json +13 -13
- package/ftm-inbox/vite.config.ts +6 -6
- package/ftm-intent/SKILL.md +241 -241
- package/ftm-intent.yml +2 -2
- package/ftm-manifest.json +3794 -3794
- package/ftm-map/SKILL.md +291 -291
- package/ftm-map/scripts/db.py +712 -712
- package/ftm-map/scripts/index.py +415 -415
- package/ftm-map/scripts/parser.py +224 -224
- package/ftm-map/scripts/queries/go-tags.scm +20 -20
- package/ftm-map/scripts/queries/javascript-tags.scm +35 -35
- package/ftm-map/scripts/queries/python-tags.scm +31 -31
- package/ftm-map/scripts/queries/ruby-tags.scm +19 -19
- package/ftm-map/scripts/queries/rust-tags.scm +37 -37
- package/ftm-map/scripts/queries/typescript-tags.scm +41 -41
- package/ftm-map/scripts/query.py +301 -301
- package/ftm-map/scripts/ranker.py +377 -377
- package/ftm-map/scripts/requirements.txt +5 -5
- package/ftm-map/scripts/setup-hooks.sh +27 -27
- package/ftm-map/scripts/setup.sh +56 -56
- package/ftm-map/scripts/test_db.py +364 -364
- package/ftm-map/scripts/test_parser.py +174 -174
- package/ftm-map/scripts/test_query.py +183 -183
- package/ftm-map/scripts/test_ranker.py +199 -199
- package/ftm-map/scripts/views.py +591 -591
- package/ftm-map.yml +2 -2
- package/ftm-mind/SKILL.md +1943 -1943
- package/ftm-mind/evals/promptfoo.yaml +142 -142
- package/ftm-mind/references/blackboard-schema.md +328 -328
- package/ftm-mind/references/complexity-guide.md +110 -110
- package/ftm-mind/references/event-registry.md +319 -319
- package/ftm-mind/references/mcp-inventory.md +296 -296
- package/ftm-mind/references/protocols/COMPLEXITY-SIZING.md +72 -72
- package/ftm-mind/references/protocols/MCP-HEURISTICS.md +32 -32
- package/ftm-mind/references/protocols/PLAN-APPROVAL.md +80 -80
- package/ftm-mind/references/reflexion-protocol.md +249 -249
- package/ftm-mind/references/routing/SCENARIOS.md +22 -22
- package/ftm-mind/references/routing-scenarios.md +35 -35
- package/ftm-mind.yml +2 -2
- package/ftm-pause/SKILL.md +395 -395
- package/ftm-pause/references/protocols/SKILL-RESTORE-PROTOCOLS.md +186 -186
- package/ftm-pause/references/protocols/VALIDATION.md +80 -80
- package/ftm-pause.yml +2 -2
- package/ftm-researcher/SKILL.md +275 -275
- package/ftm-researcher/evals/agent-diversity.yaml +17 -17
- package/ftm-researcher/evals/synthesis-quality.yaml +12 -12
- package/ftm-researcher/evals/trigger-accuracy.yaml +39 -39
- package/ftm-researcher/references/adaptive-search.md +116 -116
- package/ftm-researcher/references/agent-prompts.md +193 -193
- package/ftm-researcher/references/council-integration.md +193 -193
- package/ftm-researcher/references/output-format.md +203 -203
- package/ftm-researcher/references/synthesis-pipeline.md +165 -165
- package/ftm-researcher/scripts/score_credibility.py +234 -234
- package/ftm-researcher/scripts/validate_research.py +92 -92
- package/ftm-researcher.yml +2 -2
- package/ftm-resume/SKILL.md +518 -518
- package/ftm-resume/references/protocols/VALIDATION.md +172 -172
- package/ftm-resume.yml +2 -2
- package/ftm-retro/SKILL.md +380 -380
- package/ftm-retro/references/protocols/SCORING-RUBRICS.md +89 -89
- package/ftm-retro/references/templates/REPORT-FORMAT.md +109 -109
- package/ftm-retro.yml +2 -2
- package/ftm-routine/SKILL.md +170 -170
- package/ftm-routine.yml +4 -4
- package/ftm-state/blackboard/capabilities.json +5 -5
- package/ftm-state/blackboard/capabilities.schema.json +27 -27
- package/ftm-state/blackboard/context.json +23 -23
- package/ftm-state/blackboard/experiences/index.json +9 -9
- package/ftm-state/blackboard/patterns.json +6 -6
- package/ftm-state/schemas/context.schema.json +130 -130
- package/ftm-state/schemas/experience-index.schema.json +77 -77
- package/ftm-state/schemas/experience.schema.json +78 -78
- package/ftm-state/schemas/patterns.schema.json +44 -44
- package/ftm-upgrade/SKILL.md +194 -194
- package/ftm-upgrade/scripts/check-version.sh +76 -76
- package/ftm-upgrade/scripts/upgrade.sh +143 -143
- package/ftm-upgrade.yml +2 -2
- package/ftm-verify.yml +2 -2
- package/ftm.yml +2 -2
- package/hooks/ftm-blackboard-enforcer.sh +93 -93
- package/hooks/ftm-discovery-reminder.sh +90 -90
- package/hooks/ftm-drafts-gate.sh +61 -61
- package/hooks/ftm-event-logger.mjs +107 -107
- package/hooks/ftm-map-autodetect.sh +79 -79
- package/hooks/ftm-pending-sync-check.sh +22 -22
- package/hooks/ftm-plan-gate.sh +92 -92
- package/hooks/ftm-post-commit-trigger.sh +57 -57
- package/hooks/settings-template.json +81 -81
- package/install.sh +363 -363
- package/package.json +84 -84
- package/uninstall.sh +25 -25
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
"""
|
|
2
|
-
StepRunner — executes a single plan step via Claude CLI.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
import logging
|
|
9
|
-
import subprocess
|
|
10
|
-
import time
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
_STEP_TIMEOUT = 120 # seconds
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class StepRunner:
|
|
19
|
-
"""Runs a single plan step by invoking Claude CLI."""
|
|
20
|
-
|
|
21
|
-
@staticmethod
|
|
22
|
-
async def run(step: dict, task_context: dict) -> dict[str, Any]:
|
|
23
|
-
"""
|
|
24
|
-
Execute a plan step and return structured result.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
{"status": "completed"|"failed", "output": str, "error": str, "duration_ms": int}
|
|
28
|
-
"""
|
|
29
|
-
prompt = (
|
|
30
|
-
f"Execute this plan step:\n\n"
|
|
31
|
-
f"Step: {step.get('title', '')}\n"
|
|
32
|
-
f"Target system: {step.get('target_system', 'local')}\n"
|
|
33
|
-
f"Method: {step.get('method_primary', '')}\n"
|
|
34
|
-
f"Fallback: {step.get('method_fallback', '')}\n\n"
|
|
35
|
-
f"Task context:\n"
|
|
36
|
-
f"Title: {task_context.get('title', '')}\n"
|
|
37
|
-
f"Source: {task_context.get('source', '')}\n"
|
|
38
|
-
f"Body: {task_context.get('body', '')}\n\n"
|
|
39
|
-
f"Execute the step and report what was done. "
|
|
40
|
-
f"If the action cannot be performed, explain why."
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
start = time.monotonic()
|
|
44
|
-
try:
|
|
45
|
-
result = subprocess.run(
|
|
46
|
-
["claude", "-p", prompt, "--output-format", "json"],
|
|
47
|
-
capture_output=True,
|
|
48
|
-
text=True,
|
|
49
|
-
timeout=_STEP_TIMEOUT,
|
|
50
|
-
)
|
|
51
|
-
duration_ms = int((time.monotonic() - start) * 1000)
|
|
52
|
-
|
|
53
|
-
if result.returncode != 0:
|
|
54
|
-
return {
|
|
55
|
-
"status": "failed",
|
|
56
|
-
"output": result.stdout,
|
|
57
|
-
"error": result.stderr or f"Exit code {result.returncode}",
|
|
58
|
-
"duration_ms": duration_ms,
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Parse Claude JSON output
|
|
62
|
-
try:
|
|
63
|
-
data = json.loads(result.stdout)
|
|
64
|
-
text = data.get("result", result.stdout)
|
|
65
|
-
except json.JSONDecodeError:
|
|
66
|
-
text = result.stdout
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
"status": "completed",
|
|
70
|
-
"output": text,
|
|
71
|
-
"error": "",
|
|
72
|
-
"duration_ms": duration_ms,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
except subprocess.TimeoutExpired:
|
|
76
|
-
duration_ms = int((time.monotonic() - start) * 1000)
|
|
77
|
-
return {
|
|
78
|
-
"status": "failed",
|
|
79
|
-
"output": "",
|
|
80
|
-
"error": f"Step timed out after {_STEP_TIMEOUT}s",
|
|
81
|
-
"duration_ms": duration_ms,
|
|
82
|
-
}
|
|
83
|
-
except FileNotFoundError:
|
|
84
|
-
duration_ms = int((time.monotonic() - start) * 1000)
|
|
85
|
-
return {
|
|
86
|
-
"status": "failed",
|
|
87
|
-
"output": "",
|
|
88
|
-
"error": "Claude CLI not found on PATH",
|
|
89
|
-
"duration_ms": duration_ms,
|
|
90
|
-
}
|
|
91
|
-
except Exception as exc:
|
|
92
|
-
duration_ms = int((time.monotonic() - start) * 1000)
|
|
93
|
-
return {
|
|
94
|
-
"status": "failed",
|
|
95
|
-
"output": "",
|
|
96
|
-
"error": str(exc),
|
|
97
|
-
"duration_ms": duration_ms,
|
|
98
|
-
}
|
|
1
|
+
"""
|
|
2
|
+
StepRunner — executes a single plan step via Claude CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import subprocess
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_STEP_TIMEOUT = 120 # seconds
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StepRunner:
|
|
19
|
+
"""Runs a single plan step by invoking Claude CLI."""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
async def run(step: dict, task_context: dict) -> dict[str, Any]:
|
|
23
|
+
"""
|
|
24
|
+
Execute a plan step and return structured result.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
{"status": "completed"|"failed", "output": str, "error": str, "duration_ms": int}
|
|
28
|
+
"""
|
|
29
|
+
prompt = (
|
|
30
|
+
f"Execute this plan step:\n\n"
|
|
31
|
+
f"Step: {step.get('title', '')}\n"
|
|
32
|
+
f"Target system: {step.get('target_system', 'local')}\n"
|
|
33
|
+
f"Method: {step.get('method_primary', '')}\n"
|
|
34
|
+
f"Fallback: {step.get('method_fallback', '')}\n\n"
|
|
35
|
+
f"Task context:\n"
|
|
36
|
+
f"Title: {task_context.get('title', '')}\n"
|
|
37
|
+
f"Source: {task_context.get('source', '')}\n"
|
|
38
|
+
f"Body: {task_context.get('body', '')}\n\n"
|
|
39
|
+
f"Execute the step and report what was done. "
|
|
40
|
+
f"If the action cannot be performed, explain why."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
start = time.monotonic()
|
|
44
|
+
try:
|
|
45
|
+
result = subprocess.run(
|
|
46
|
+
["claude", "-p", prompt, "--output-format", "json"],
|
|
47
|
+
capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
timeout=_STEP_TIMEOUT,
|
|
50
|
+
)
|
|
51
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
52
|
+
|
|
53
|
+
if result.returncode != 0:
|
|
54
|
+
return {
|
|
55
|
+
"status": "failed",
|
|
56
|
+
"output": result.stdout,
|
|
57
|
+
"error": result.stderr or f"Exit code {result.returncode}",
|
|
58
|
+
"duration_ms": duration_ms,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Parse Claude JSON output
|
|
62
|
+
try:
|
|
63
|
+
data = json.loads(result.stdout)
|
|
64
|
+
text = data.get("result", result.stdout)
|
|
65
|
+
except json.JSONDecodeError:
|
|
66
|
+
text = result.stdout
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"status": "completed",
|
|
70
|
+
"output": text,
|
|
71
|
+
"error": "",
|
|
72
|
+
"duration_ms": duration_ms,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
except subprocess.TimeoutExpired:
|
|
76
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
77
|
+
return {
|
|
78
|
+
"status": "failed",
|
|
79
|
+
"output": "",
|
|
80
|
+
"error": f"Step timed out after {_STEP_TIMEOUT}s",
|
|
81
|
+
"duration_ms": duration_ms,
|
|
82
|
+
}
|
|
83
|
+
except FileNotFoundError:
|
|
84
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
85
|
+
return {
|
|
86
|
+
"status": "failed",
|
|
87
|
+
"output": "",
|
|
88
|
+
"error": "Claude CLI not found on PATH",
|
|
89
|
+
"duration_ms": duration_ms,
|
|
90
|
+
}
|
|
91
|
+
except Exception as exc:
|
|
92
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
93
|
+
return {
|
|
94
|
+
"status": "failed",
|
|
95
|
+
"output": "",
|
|
96
|
+
"error": str(exc),
|
|
97
|
+
"duration_ms": duration_ms,
|
|
98
|
+
}
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
"""
|
|
2
|
-
ftm-inbox FastAPI application entry point.
|
|
3
|
-
|
|
4
|
-
Default port: 8042 (override via FTM_INBOX_PORT env var).
|
|
5
|
-
|
|
6
|
-
Startup sequence:
|
|
7
|
-
1. Open SQLite connection (WAL mode)
|
|
8
|
-
2. Initialize schema (idempotent CREATE TABLE IF NOT EXISTS)
|
|
9
|
-
3. Load adapter registry from config.yml (warn if missing, don't crash)
|
|
10
|
-
4. Register API routes
|
|
11
|
-
|
|
12
|
-
CORS allows all localhost origins for development.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
import logging
|
|
16
|
-
import os
|
|
17
|
-
|
|
18
|
-
from fastapi import FastAPI
|
|
19
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
20
|
-
|
|
21
|
-
from backend.db.connection import get_connection
|
|
22
|
-
from backend.db.schema import initialize_schema
|
|
23
|
-
from backend.routes.health import router as health_router
|
|
24
|
-
from backend.routes.inbox import router as inbox_router
|
|
25
|
-
from backend.routes.plan import router as plan_router
|
|
26
|
-
from backend.routes.execute import router as execute_router
|
|
27
|
-
|
|
28
|
-
logging.basicConfig(
|
|
29
|
-
level=logging.INFO,
|
|
30
|
-
format="%(asctime)s %(levelname)-8s %(name)s %(message)s",
|
|
31
|
-
)
|
|
32
|
-
logger = logging.getLogger("ftm-inbox")
|
|
33
|
-
|
|
34
|
-
app = FastAPI(
|
|
35
|
-
title="FTM Inbox",
|
|
36
|
-
description="Operator Cockpit backend — aggregates tasks from all connected sources.",
|
|
37
|
-
version="0.1.0",
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
# CORS: allow all localhost origins (frontend dev server + any local tooling)
|
|
41
|
-
app.add_middleware(
|
|
42
|
-
CORSMiddleware,
|
|
43
|
-
allow_origins=[
|
|
44
|
-
"http://localhost:5173",
|
|
45
|
-
"http://localhost:3000",
|
|
46
|
-
"http://127.0.0.1:5173",
|
|
47
|
-
"http://127.0.0.1:3000",
|
|
48
|
-
],
|
|
49
|
-
allow_credentials=True,
|
|
50
|
-
allow_methods=["*"],
|
|
51
|
-
allow_headers=["*"],
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
# ---------------------------------------------------------------------------
|
|
55
|
-
# Startup
|
|
56
|
-
# ---------------------------------------------------------------------------
|
|
57
|
-
|
|
58
|
-
@app.on_event("startup")
|
|
59
|
-
async def startup() -> None:
|
|
60
|
-
logger.info("Starting ftm-inbox backend…")
|
|
61
|
-
|
|
62
|
-
# 1. Open DB and initialize schema
|
|
63
|
-
conn = get_connection()
|
|
64
|
-
initialize_schema(conn)
|
|
65
|
-
logger.info("Database initialized.")
|
|
66
|
-
|
|
67
|
-
# 2. Load adapter registry (non-fatal if config.yml is missing)
|
|
68
|
-
try:
|
|
69
|
-
from backend.adapters.registry import AdapterRegistry
|
|
70
|
-
registry = AdapterRegistry.from_config()
|
|
71
|
-
app.state.adapter_registry = registry
|
|
72
|
-
logger.info("Adapter registry loaded: %s", registry)
|
|
73
|
-
except Exception as exc:
|
|
74
|
-
logger.warning("Adapter registry failed to load: %s", exc)
|
|
75
|
-
app.state.adapter_registry = None
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@app.on_event("shutdown")
|
|
79
|
-
async def shutdown() -> None:
|
|
80
|
-
from backend.db.connection import close_connection
|
|
81
|
-
close_connection()
|
|
82
|
-
logger.info("Database connection closed.")
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# ---------------------------------------------------------------------------
|
|
86
|
-
# Routes
|
|
87
|
-
# ---------------------------------------------------------------------------
|
|
88
|
-
|
|
89
|
-
app.include_router(health_router)
|
|
90
|
-
app.include_router(inbox_router)
|
|
91
|
-
app.include_router(plan_router)
|
|
92
|
-
app.include_router(execute_router)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# ---------------------------------------------------------------------------
|
|
96
|
-
# Dev entrypoint
|
|
97
|
-
# ---------------------------------------------------------------------------
|
|
98
|
-
|
|
99
|
-
if __name__ == "__main__":
|
|
100
|
-
import uvicorn
|
|
101
|
-
|
|
102
|
-
port = int(os.environ.get("FTM_INBOX_PORT", "8042"))
|
|
103
|
-
uvicorn.run("backend.main:app", host="0.0.0.0", port=port, reload=True)
|
|
1
|
+
"""
|
|
2
|
+
ftm-inbox FastAPI application entry point.
|
|
3
|
+
|
|
4
|
+
Default port: 8042 (override via FTM_INBOX_PORT env var).
|
|
5
|
+
|
|
6
|
+
Startup sequence:
|
|
7
|
+
1. Open SQLite connection (WAL mode)
|
|
8
|
+
2. Initialize schema (idempotent CREATE TABLE IF NOT EXISTS)
|
|
9
|
+
3. Load adapter registry from config.yml (warn if missing, don't crash)
|
|
10
|
+
4. Register API routes
|
|
11
|
+
|
|
12
|
+
CORS allows all localhost origins for development.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from fastapi import FastAPI
|
|
19
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
20
|
+
|
|
21
|
+
from backend.db.connection import get_connection
|
|
22
|
+
from backend.db.schema import initialize_schema
|
|
23
|
+
from backend.routes.health import router as health_router
|
|
24
|
+
from backend.routes.inbox import router as inbox_router
|
|
25
|
+
from backend.routes.plan import router as plan_router
|
|
26
|
+
from backend.routes.execute import router as execute_router
|
|
27
|
+
|
|
28
|
+
logging.basicConfig(
|
|
29
|
+
level=logging.INFO,
|
|
30
|
+
format="%(asctime)s %(levelname)-8s %(name)s %(message)s",
|
|
31
|
+
)
|
|
32
|
+
logger = logging.getLogger("ftm-inbox")
|
|
33
|
+
|
|
34
|
+
app = FastAPI(
|
|
35
|
+
title="FTM Inbox",
|
|
36
|
+
description="Operator Cockpit backend — aggregates tasks from all connected sources.",
|
|
37
|
+
version="0.1.0",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# CORS: allow all localhost origins (frontend dev server + any local tooling)
|
|
41
|
+
app.add_middleware(
|
|
42
|
+
CORSMiddleware,
|
|
43
|
+
allow_origins=[
|
|
44
|
+
"http://localhost:5173",
|
|
45
|
+
"http://localhost:3000",
|
|
46
|
+
"http://127.0.0.1:5173",
|
|
47
|
+
"http://127.0.0.1:3000",
|
|
48
|
+
],
|
|
49
|
+
allow_credentials=True,
|
|
50
|
+
allow_methods=["*"],
|
|
51
|
+
allow_headers=["*"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Startup
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
@app.on_event("startup")
|
|
59
|
+
async def startup() -> None:
|
|
60
|
+
logger.info("Starting ftm-inbox backend…")
|
|
61
|
+
|
|
62
|
+
# 1. Open DB and initialize schema
|
|
63
|
+
conn = get_connection()
|
|
64
|
+
initialize_schema(conn)
|
|
65
|
+
logger.info("Database initialized.")
|
|
66
|
+
|
|
67
|
+
# 2. Load adapter registry (non-fatal if config.yml is missing)
|
|
68
|
+
try:
|
|
69
|
+
from backend.adapters.registry import AdapterRegistry
|
|
70
|
+
registry = AdapterRegistry.from_config()
|
|
71
|
+
app.state.adapter_registry = registry
|
|
72
|
+
logger.info("Adapter registry loaded: %s", registry)
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
logger.warning("Adapter registry failed to load: %s", exc)
|
|
75
|
+
app.state.adapter_registry = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@app.on_event("shutdown")
|
|
79
|
+
async def shutdown() -> None:
|
|
80
|
+
from backend.db.connection import close_connection
|
|
81
|
+
close_connection()
|
|
82
|
+
logger.info("Database connection closed.")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Routes
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
app.include_router(health_router)
|
|
90
|
+
app.include_router(inbox_router)
|
|
91
|
+
app.include_router(plan_router)
|
|
92
|
+
app.include_router(execute_router)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# Dev entrypoint
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
import uvicorn
|
|
101
|
+
|
|
102
|
+
port = int(os.environ.get("FTM_INBOX_PORT", "8042"))
|
|
103
|
+
uvicorn.run("backend.main:app", host="0.0.0.0", port=port, reload=True)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"""ftm-inbox data models."""
|
|
1
|
+
"""ftm-inbox data models."""
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
"""
|
|
2
|
-
UnifiedTask — canonical Pydantic model for a task regardless of source.
|
|
3
|
-
|
|
4
|
-
This is the read/API-facing model. The write path uses NormalizedItem (a dataclass)
|
|
5
|
-
for speed; UnifiedTask is the model returned to callers and stored as JSON.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from pydantic import BaseModel, Field
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class UnifiedTask(BaseModel):
|
|
16
|
-
"""Single unified representation of a task from any integrated source."""
|
|
17
|
-
|
|
18
|
-
id: int | None = None
|
|
19
|
-
source: str
|
|
20
|
-
source_id: str
|
|
21
|
-
title: str
|
|
22
|
-
body: str = ""
|
|
23
|
-
status: str = "open"
|
|
24
|
-
priority: str = "medium"
|
|
25
|
-
assignee: str | None = None
|
|
26
|
-
requester: str | None = None
|
|
27
|
-
created_at: str | None = None
|
|
28
|
-
updated_at: str | None = None
|
|
29
|
-
tags: list[str] = Field(default_factory=list)
|
|
30
|
-
custom_fields: dict[str, Any] = Field(default_factory=dict)
|
|
31
|
-
raw_payload: dict[str, Any] = Field(default_factory=dict)
|
|
32
|
-
source_url: str | None = None
|
|
33
|
-
content_hash: str | None = None
|
|
34
|
-
ingested_at: str | None = None
|
|
35
|
-
|
|
36
|
-
model_config = {"populate_by_name": True}
|
|
1
|
+
"""
|
|
2
|
+
UnifiedTask — canonical Pydantic model for a task regardless of source.
|
|
3
|
+
|
|
4
|
+
This is the read/API-facing model. The write path uses NormalizedItem (a dataclass)
|
|
5
|
+
for speed; UnifiedTask is the model returned to callers and stored as JSON.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UnifiedTask(BaseModel):
|
|
16
|
+
"""Single unified representation of a task from any integrated source."""
|
|
17
|
+
|
|
18
|
+
id: int | None = None
|
|
19
|
+
source: str
|
|
20
|
+
source_id: str
|
|
21
|
+
title: str
|
|
22
|
+
body: str = ""
|
|
23
|
+
status: str = "open"
|
|
24
|
+
priority: str = "medium"
|
|
25
|
+
assignee: str | None = None
|
|
26
|
+
requester: str | None = None
|
|
27
|
+
created_at: str | None = None
|
|
28
|
+
updated_at: str | None = None
|
|
29
|
+
tags: list[str] = Field(default_factory=list)
|
|
30
|
+
custom_fields: dict[str, Any] = Field(default_factory=dict)
|
|
31
|
+
raw_payload: dict[str, Any] = Field(default_factory=dict)
|
|
32
|
+
source_url: str | None = None
|
|
33
|
+
content_hash: str | None = None
|
|
34
|
+
ingested_at: str | None = None
|
|
35
|
+
|
|
36
|
+
model_config = {"populate_by_name": True}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""
|
|
2
|
-
ftm-inbox planner package.
|
|
3
|
-
|
|
4
|
-
Generates structured YAML execution plans from task payloads via Claude CLI,
|
|
5
|
-
and persists them to SQLite for per-step approval workflows.
|
|
6
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
ftm-inbox planner package.
|
|
3
|
+
|
|
4
|
+
Generates structured YAML execution plans from task payloads via Claude CLI,
|
|
5
|
+
and persists them to SQLite for per-step approval workflows.
|
|
6
|
+
"""
|