@fml-inc/fml 0.7.0 → 0.8.0-rc.2

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 (45) hide show
  1. package/.claude-plugin/plugin.json +2 -4
  2. package/README.md +323 -79
  3. package/agent-assets/fml/SKILL.md +180 -0
  4. package/bin/fml +4 -18
  5. package/bin/hook-handler +7 -0
  6. package/bin/mcp-server +4 -18
  7. package/bin/observer +6 -0
  8. package/dist/chunk-3KISRT57.js +140 -0
  9. package/dist/chunk-56TPYMNK.js +4657 -0
  10. package/dist/chunk-7V7G7NVF.js +142 -0
  11. package/dist/chunk-A6HAEVJX.js +1606 -0
  12. package/dist/chunk-DZKYW327.js +1037 -0
  13. package/dist/chunk-ENW2GJNO.js +109 -0
  14. package/dist/{chunk-PZ5AY32C.js → chunk-MLKGABMK.js} +0 -1
  15. package/dist/{chunk-PTUZMKSN.js → chunk-R45CWWZO.js} +34 -23
  16. package/dist/chunk-WZLLO563.js +953 -0
  17. package/dist/chunk-YSU5CS2V.js +3331 -0
  18. package/dist/cli.js +7742 -1072
  19. package/dist/{esm-2EUDFZWP.js → esm-4YKLKFRX.js} +546 -741
  20. package/dist/hook-handler.js +440 -0
  21. package/dist/mcp/server.js +112 -98
  22. package/dist/observer-server.js +1566 -0
  23. package/dist/sync-cycle.js +31 -0
  24. package/dist/{token-store-3IQSLOTQ.js → token-store-BT5THIAJ.js} +10 -3
  25. package/hooks/hooks.json +290 -0
  26. package/package.json +24 -27
  27. package/dist/chunk-PTUZMKSN.js.map +0 -1
  28. package/dist/chunk-PZ5AY32C.js.map +0 -1
  29. package/dist/chunk-S4TJNLEN.js +0 -61
  30. package/dist/chunk-S4TJNLEN.js.map +0 -1
  31. package/dist/chunk-VV4JHH4H.js +0 -437
  32. package/dist/chunk-VV4JHH4H.js.map +0 -1
  33. package/dist/chunk-YH5ROR7G.js +0 -69
  34. package/dist/chunk-YH5ROR7G.js.map +0 -1
  35. package/dist/cli.js.map +0 -1
  36. package/dist/daemon-MSLDHLMH.js +0 -21
  37. package/dist/daemon-MSLDHLMH.js.map +0 -1
  38. package/dist/daemon-utils-OODBIYB7.js +0 -10
  39. package/dist/daemon-utils-OODBIYB7.js.map +0 -1
  40. package/dist/esm-2EUDFZWP.js.map +0 -1
  41. package/dist/mcp/server.js.map +0 -1
  42. package/dist/postinstall.js +0 -16
  43. package/dist/postinstall.js.map +0 -1
  44. package/dist/token-store-3IQSLOTQ.js.map +0 -1
  45. package/scripts/postinstall.cjs +0 -25
@@ -0,0 +1,142 @@
1
+ // src/repo.ts
2
+ import { execFile, execFileSync } from "child_process";
3
+ var REPO_CACHE = /* @__PURE__ */ new Map();
4
+ var ASYNC_REPO_CACHE = /* @__PURE__ */ new Map();
5
+ var GIT_TIMEOUT_MS = 5e3;
6
+ var POSITIVE_REPO_CACHE_TTL_MS = 3e4;
7
+ var NEGATIVE_REPO_CACHE_TTL_MS = 1e3;
8
+ var MAX_REPO_CACHE_ENTRIES = 256;
9
+ function cachedRepo(cwd) {
10
+ const entry = REPO_CACHE.get(cwd);
11
+ if (!entry) return { hit: false };
12
+ if (entry.expiresAtMs <= Date.now()) {
13
+ REPO_CACHE.delete(cwd);
14
+ return { hit: false };
15
+ }
16
+ REPO_CACHE.delete(cwd);
17
+ REPO_CACHE.set(cwd, entry);
18
+ return { hit: true, value: entry.value };
19
+ }
20
+ function cacheRepo(cwd, value) {
21
+ REPO_CACHE.delete(cwd);
22
+ while (REPO_CACHE.size >= MAX_REPO_CACHE_ENTRIES) {
23
+ const oldest = REPO_CACHE.keys().next().value;
24
+ if (oldest === void 0) break;
25
+ REPO_CACHE.delete(oldest);
26
+ }
27
+ REPO_CACHE.set(cwd, {
28
+ value,
29
+ expiresAtMs: Date.now() + (value === null ? NEGATIVE_REPO_CACHE_TTL_MS : POSITIVE_REPO_CACHE_TTL_MS)
30
+ });
31
+ }
32
+ function git(cwd, args, timeoutMs) {
33
+ try {
34
+ return execFileSync("git", ["-C", cwd, ...args], {
35
+ encoding: "utf8",
36
+ timeout: timeoutMs,
37
+ stdio: ["ignore", "pipe", "ignore"],
38
+ windowsHide: true
39
+ }).trim() || null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ function gitAsync(cwd, args, signal) {
45
+ return new Promise((resolve, reject) => {
46
+ try {
47
+ execFile(
48
+ "git",
49
+ ["-C", cwd, ...args],
50
+ {
51
+ encoding: "utf8",
52
+ timeout: GIT_TIMEOUT_MS,
53
+ windowsHide: true,
54
+ ...signal ? { signal } : {}
55
+ },
56
+ (error, stdout) => {
57
+ if (signal?.aborted) {
58
+ reject(signal.reason ?? new Error("Repository lookup aborted"));
59
+ return;
60
+ }
61
+ resolve(error ? null : stdout.trim() || null);
62
+ }
63
+ );
64
+ } catch (error) {
65
+ if (signal?.aborted) {
66
+ reject(signal.reason ?? error);
67
+ return;
68
+ }
69
+ resolve(null);
70
+ }
71
+ });
72
+ }
73
+ function repositorySlug(remote) {
74
+ const ssh = remote.match(/^[^@]+@[^:]+:([^/]+\/[^/]+?)(?:\.git)?$/);
75
+ if (ssh?.[1]) return ssh[1];
76
+ try {
77
+ const url = new URL(remote);
78
+ const slug = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
79
+ return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(slug) ? slug : null;
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
84
+ function resolveRepoFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
85
+ const cached = cachedRepo(cwd);
86
+ if (cached.hit) return cached.value;
87
+ const remote = git(cwd, ["remote", "get-url", "origin"], timeoutMs);
88
+ const repo = remote ? repositorySlug(remote) : null;
89
+ const result = repo ? {
90
+ repo,
91
+ branch: git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], timeoutMs)
92
+ } : null;
93
+ cacheRepo(cwd, result);
94
+ return result;
95
+ }
96
+ function resolveRepositorySlugFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
97
+ const cached = cachedRepo(cwd);
98
+ if (cached.hit) return cached.value?.repo ?? null;
99
+ const remote = git(cwd, ["remote", "get-url", "origin"], timeoutMs);
100
+ return remote ? repositorySlug(remote) : null;
101
+ }
102
+ function resolveRepositoryRootFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
103
+ return git(cwd, ["rev-parse", "--show-toplevel"], timeoutMs);
104
+ }
105
+ async function resolveRepoFromCwdAsync(cwd, signal) {
106
+ if (signal?.aborted) {
107
+ throw signal.reason ?? new Error("Repository lookup aborted");
108
+ }
109
+ const cached = cachedRepo(cwd);
110
+ if (cached.hit) return cached.value;
111
+ const cachedResolution = ASYNC_REPO_CACHE.get(cwd);
112
+ if (cachedResolution) return await cachedResolution;
113
+ const resolution = (async () => {
114
+ const remote = await gitAsync(cwd, ["remote", "get-url", "origin"], signal);
115
+ const repo = remote ? repositorySlug(remote) : null;
116
+ const result = repo ? {
117
+ repo,
118
+ branch: await gitAsync(
119
+ cwd,
120
+ ["rev-parse", "--abbrev-ref", "HEAD"],
121
+ signal
122
+ )
123
+ } : null;
124
+ cacheRepo(cwd, result);
125
+ return result;
126
+ })();
127
+ ASYNC_REPO_CACHE.set(cwd, resolution);
128
+ try {
129
+ return await resolution;
130
+ } finally {
131
+ if (ASYNC_REPO_CACHE.get(cwd) === resolution) {
132
+ ASYNC_REPO_CACHE.delete(cwd);
133
+ }
134
+ }
135
+ }
136
+
137
+ export {
138
+ resolveRepoFromCwd,
139
+ resolveRepositorySlugFromCwd,
140
+ resolveRepositoryRootFromCwd,
141
+ resolveRepoFromCwdAsync
142
+ };