@jitera/connect 0.1.1 → 1.2.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.
Files changed (115) hide show
  1. package/.claude-plugin/plugin.json +12 -9
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +17 -3
  4. package/dist/adapters/codex.d.ts +4 -0
  5. package/dist/adapters/codex.d.ts.map +1 -0
  6. package/dist/adapters/codex.js +49 -0
  7. package/dist/adapters/codex.js.map +1 -0
  8. package/dist/adapters/cursor.d.ts +4 -0
  9. package/dist/adapters/cursor.d.ts.map +1 -0
  10. package/dist/adapters/cursor.js +47 -0
  11. package/dist/adapters/cursor.js.map +1 -0
  12. package/dist/adapters/types.d.ts +25 -0
  13. package/dist/adapters/types.d.ts.map +1 -0
  14. package/dist/adapters/types.js +2 -0
  15. package/dist/adapters/types.js.map +1 -0
  16. package/dist/bin/cli.d.ts +3 -0
  17. package/dist/bin/cli.d.ts.map +1 -0
  18. package/dist/bin/cli.js +164 -0
  19. package/dist/bin/cli.js.map +1 -0
  20. package/dist/bin/login.d.ts +3 -0
  21. package/dist/bin/login.d.ts.map +1 -0
  22. package/dist/bin/login.js +181 -0
  23. package/dist/bin/login.js.map +1 -0
  24. package/dist/bin/proxy.d.ts +3 -0
  25. package/dist/bin/proxy.d.ts.map +1 -0
  26. package/dist/bin/proxy.js +19 -0
  27. package/dist/bin/proxy.js.map +1 -0
  28. package/dist/bin/validate.d.ts +3 -0
  29. package/dist/bin/validate.d.ts.map +1 -0
  30. package/dist/bin/validate.js +13 -0
  31. package/dist/bin/validate.js.map +1 -0
  32. package/dist/device-flow.d.ts +30 -0
  33. package/dist/device-flow.d.ts.map +1 -0
  34. package/dist/device-flow.js +105 -0
  35. package/dist/device-flow.js.map +1 -0
  36. package/dist/discovery.d.ts +21 -0
  37. package/dist/discovery.d.ts.map +1 -0
  38. package/dist/discovery.js +58 -0
  39. package/dist/discovery.js.map +1 -0
  40. package/dist/environments.d.ts +18 -0
  41. package/dist/environments.d.ts.map +1 -0
  42. package/dist/environments.js +45 -0
  43. package/dist/environments.js.map +1 -0
  44. package/dist/graphql.d.ts +31 -0
  45. package/dist/graphql.d.ts.map +1 -0
  46. package/dist/graphql.js +100 -0
  47. package/dist/graphql.js.map +1 -0
  48. package/dist/hook-io.d.ts +22 -0
  49. package/dist/hook-io.d.ts.map +1 -0
  50. package/dist/hook-io.js +13 -0
  51. package/dist/hook-io.js.map +1 -0
  52. package/dist/hooks/session-start.d.ts +3 -0
  53. package/dist/hooks/session-start.d.ts.map +1 -0
  54. package/dist/hooks/session-start.js +16 -0
  55. package/dist/hooks/session-start.js.map +1 -0
  56. package/dist/hooks/stop.d.ts +3 -0
  57. package/dist/hooks/stop.d.ts.map +1 -0
  58. package/dist/hooks/stop.js +21 -0
  59. package/dist/hooks/stop.js.map +1 -0
  60. package/dist/hooks/user-prompt-submit.d.ts +3 -0
  61. package/dist/hooks/user-prompt-submit.d.ts.map +1 -0
  62. package/dist/hooks/user-prompt-submit.js +55 -0
  63. package/dist/hooks/user-prompt-submit.js.map +1 -0
  64. package/dist/install/agents-md.d.ts +25 -0
  65. package/dist/install/agents-md.d.ts.map +1 -0
  66. package/dist/install/agents-md.js +58 -0
  67. package/dist/install/agents-md.js.map +1 -0
  68. package/dist/install/claude-code.d.ts +20 -0
  69. package/dist/install/claude-code.d.ts.map +1 -0
  70. package/dist/install/claude-code.js +33 -0
  71. package/dist/install/claude-code.js.map +1 -0
  72. package/dist/install/render.d.ts +13 -0
  73. package/dist/install/render.d.ts.map +1 -0
  74. package/dist/install/render.js +22 -0
  75. package/dist/install/render.js.map +1 -0
  76. package/dist/install/skills.d.ts +16 -0
  77. package/dist/install/skills.d.ts.map +1 -0
  78. package/dist/install/skills.js +62 -0
  79. package/dist/install/skills.js.map +1 -0
  80. package/dist/manifest.d.ts +62 -0
  81. package/dist/manifest.d.ts.map +1 -0
  82. package/dist/manifest.js +5 -0
  83. package/dist/manifest.js.map +1 -0
  84. package/dist/mcp-client.d.ts +43 -0
  85. package/dist/mcp-client.d.ts.map +1 -0
  86. package/dist/mcp-client.js +85 -0
  87. package/dist/mcp-client.js.map +1 -0
  88. package/dist/mcp-config.d.ts +12 -0
  89. package/dist/mcp-config.d.ts.map +1 -0
  90. package/dist/mcp-config.js +45 -0
  91. package/dist/mcp-config.js.map +1 -0
  92. package/dist/proxy.d.ts +12 -0
  93. package/dist/proxy.d.ts.map +1 -0
  94. package/dist/proxy.js +54 -0
  95. package/dist/proxy.js.map +1 -0
  96. package/dist/session-marker.d.ts +4 -0
  97. package/dist/session-marker.d.ts.map +1 -0
  98. package/dist/session-marker.js +25 -0
  99. package/dist/session-marker.js.map +1 -0
  100. package/dist/toml-section.d.ts +12 -0
  101. package/dist/toml-section.d.ts.map +1 -0
  102. package/dist/toml-section.js +66 -0
  103. package/dist/toml-section.js.map +1 -0
  104. package/dist/validate.d.ts +11 -0
  105. package/dist/validate.d.ts.map +1 -0
  106. package/dist/validate.js +58 -0
  107. package/dist/validate.js.map +1 -0
  108. package/hooks/hooks.json +15 -2
  109. package/package.json +13 -6
  110. package/bin/jitera-connect.mjs +0 -120
  111. package/hooks/scripts/session-start.mjs +0 -36
  112. package/hooks/scripts/stop.mjs +0 -39
  113. package/src/adapters/cursor.mjs +0 -56
  114. package/src/environments.mjs +0 -46
  115. package/src/mcp-config.mjs +0 -45
@@ -0,0 +1,45 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ export class MalformedConfigError extends Error {
4
+ name = "MalformedConfigError";
5
+ path;
6
+ constructor(path, cause) {
7
+ super(`${path} exists but is not valid JSON, so it cannot be merged safely. ` +
8
+ `Fix or move the file, then run again. Parser said: ${cause.message}`);
9
+ this.path = path;
10
+ }
11
+ }
12
+ export function readConfig(path) {
13
+ if (!existsSync(path))
14
+ return {};
15
+ const raw = readFileSync(path, "utf8");
16
+ if (raw.trim() === "")
17
+ return {};
18
+ try {
19
+ return JSON.parse(raw);
20
+ }
21
+ catch (cause) {
22
+ throw new MalformedConfigError(path, cause);
23
+ }
24
+ }
25
+ export function mergeServer(config, key, name, server) {
26
+ const servers = { ...(config[key] ?? {}) };
27
+ servers[name] = server;
28
+ return { ...config, [key]: servers };
29
+ }
30
+ export function removeServer(config, key, name) {
31
+ const existing = config[key];
32
+ if (!existing)
33
+ return { ...config };
34
+ const servers = { ...existing };
35
+ delete servers[name];
36
+ const next = { ...config, [key]: servers };
37
+ if (Object.keys(servers).length === 0)
38
+ delete next[key];
39
+ return next;
40
+ }
41
+ export function writeConfig(path, config) {
42
+ mkdirSync(dirname(path), { recursive: true });
43
+ writeFileSync(path, `${JSON.stringify(config, undefined, 2)}\n`, "utf8");
44
+ }
45
+ //# sourceMappingURL=mcp-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../src/mcp-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC3B,IAAI,GAAG,sBAAsB,CAAC;IACvC,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,KAAY;QACpC,KAAK,CACH,GAAG,IAAI,gEAAgE;YACrE,sDAAsD,KAAK,CAAC,OAAO,EAAE,CACxE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,oBAAoB,CAAC,IAAI,EAAE,KAAc,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,MAAoB,EACpB,GAAW,EACX,IAAY,EACZ,MAAmB;IAEnB,MAAM,OAAO,GAAG,EAAE,GAAG,CAAE,MAAM,CAAC,GAAG,CAA6C,IAAI,EAAE,CAAC,EAAE,CAAC;IACxF,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,GAAW,EAAE,IAAY;IAC1E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAA4C,CAAC;IACxE,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAChC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAoB;IAC5D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface ProxyStreams {
2
+ readonly input: NodeJS.ReadableStream;
3
+ readonly output: NodeJS.WritableStream;
4
+ readonly log: NodeJS.WritableStream;
5
+ }
6
+ export interface ProxyConfig {
7
+ readonly url: string;
8
+ readonly apiKey: string;
9
+ }
10
+ export declare function runProxy({ url, apiKey }: ProxyConfig, { input, output, log }: ProxyStreams): Promise<void>;
11
+ export declare function configFromEnvironment(env: NodeJS.ProcessEnv): Promise<ProxyConfig>;
12
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,cAAc,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAUD,wBAAsB,QAAQ,CAC5B,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,WAAW,EAC5B,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,YAAY,GACnC,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAWxF"}
package/dist/proxy.js ADDED
@@ -0,0 +1,54 @@
1
+ import { createInterface } from "node:readline";
2
+ import { discoverDeployment } from "./discovery.js";
3
+ import { McpCallError, postRpc } from "./mcp-client.js";
4
+ const REQUEST_TIMEOUT_MS = 30_000;
5
+ function isNotification(request) {
6
+ return request.id === undefined || request.id === null;
7
+ }
8
+ function errorResponse(id, code, message) {
9
+ return JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
10
+ }
11
+ export async function runProxy({ url, apiKey }, { input, output, log }) {
12
+ const lines = createInterface({ input, crlfDelay: Infinity });
13
+ for await (const line of lines) {
14
+ const trimmed = line.trim();
15
+ if (!trimmed)
16
+ continue;
17
+ let request;
18
+ try {
19
+ request = JSON.parse(trimmed);
20
+ }
21
+ catch {
22
+ log.write(`jitera-connect proxy: ignoring unparseable line\n`);
23
+ continue;
24
+ }
25
+ let response;
26
+ try {
27
+ response = await postRpc(request, { url, apiKey, timeoutMs: REQUEST_TIMEOUT_MS });
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof McpCallError ? error.message : String(error);
31
+ log.write(`jitera-connect proxy: ${message}\n`);
32
+ if (!isNotification(request)) {
33
+ output.write(`${errorResponse(request.id, -32603, message)}\n`);
34
+ }
35
+ continue;
36
+ }
37
+ if (!isNotification(request) && response) {
38
+ output.write(`${JSON.stringify(response)}\n`);
39
+ }
40
+ }
41
+ }
42
+ export async function configFromEnvironment(env) {
43
+ const apiKey = env["JITERA_API_KEY"] ?? "";
44
+ const override = env["JITERA_MCP_URL"] ?? "";
45
+ if (override)
46
+ return { url: override, apiKey };
47
+ const environment = env["JITERA_ENVIRONMENT"] ?? "";
48
+ const deployment = await discoverDeployment({
49
+ environment,
50
+ studioUrl: env["JITERA_STUDIO_URL"],
51
+ });
52
+ return { url: deployment.mcpUrl, apiKey };
53
+ }
54
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type MarkerKind = "recall";
2
+ export declare function markerPath(sessionId: string, kind: MarkerKind, root?: string): string;
3
+ export declare function claimOnce(sessionId: string | undefined, kind: MarkerKind, root?: string): boolean;
4
+ //# sourceMappingURL=session-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-marker.d.ts","sourceRoot":"","sources":["../src/session-marker.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;AAElC,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,GAAE,MAAiB,GAAG,MAAM,CAG/F;AAED,wBAAgB,SAAS,CACvB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,IAAI,EAAE,UAAU,EAChB,IAAI,GAAE,MAAiB,GACtB,OAAO,CAWT"}
@@ -0,0 +1,25 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createHash } from "node:crypto";
5
+ const NAMESPACE = "jitera-connect";
6
+ export function markerPath(sessionId, kind, root = tmpdir()) {
7
+ const digest = createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
8
+ return join(root, NAMESPACE, `${kind}-${digest}`);
9
+ }
10
+ export function claimOnce(sessionId, kind, root = tmpdir()) {
11
+ if (!sessionId)
12
+ return false;
13
+ const path = markerPath(sessionId, kind, root);
14
+ if (existsSync(path))
15
+ return false;
16
+ try {
17
+ mkdirSync(join(root, NAMESPACE), { recursive: true });
18
+ writeFileSync(path, "", { flag: "wx" });
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ //# sourceMappingURL=session-marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-marker.js","sourceRoot":"","sources":["../src/session-marker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAInC,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,IAAgB,EAAE,IAAI,GAAW,MAAM,EAAE;IACrF,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,SAA6B,EAC7B,IAAgB,EAChB,IAAI,GAAW,MAAM,EAAE;IAEvB,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface TomlSection {
2
+ readonly header: string | undefined;
3
+ readonly lines: readonly string[];
4
+ }
5
+ export declare function splitSections(source: string): TomlSection[];
6
+ export declare function joinSections(sections: readonly TomlSection[]): string;
7
+ export declare function ownsHeader(header: string | undefined, table: string): boolean;
8
+ export declare function removeTable(source: string, table: string): string;
9
+ export declare function upsertTable(source: string, table: string, body: string): string;
10
+ export declare function hasRootKey(source: string, key: string): boolean;
11
+ export declare function ensureRootKey(source: string, key: string, value: string): string;
12
+ //# sourceMappingURL=toml-section.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toml-section.d.ts","sourceRoot":"","sources":["../src/toml-section.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAUD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAkB3D;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAErE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/E;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAK/D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAchF"}
@@ -0,0 +1,66 @@
1
+ const SECTION_HEADER = /^\s*\[\[?([^\]]+)\]\]?\s*$/;
2
+ const COMMENT_LINE = /^\s*#/;
3
+ function peelLeadingRun(lines) {
4
+ let index = lines.length;
5
+ while (index > 0 && COMMENT_LINE.test(lines[index - 1] ?? ""))
6
+ index -= 1;
7
+ return lines.splice(index);
8
+ }
9
+ export function splitSections(source) {
10
+ const sections = [];
11
+ let header;
12
+ let lines = [];
13
+ for (const line of source.split("\n")) {
14
+ const match = SECTION_HEADER.exec(line);
15
+ if (match) {
16
+ const carried = peelLeadingRun(lines);
17
+ sections.push({ header, lines });
18
+ header = match[1]?.trim();
19
+ lines = [...carried, line];
20
+ }
21
+ else {
22
+ lines.push(line);
23
+ }
24
+ }
25
+ sections.push({ header, lines });
26
+ return sections;
27
+ }
28
+ export function joinSections(sections) {
29
+ return sections.flatMap((section) => section.lines).join("\n");
30
+ }
31
+ export function ownsHeader(header, table) {
32
+ return header === table || (header?.startsWith(`${table}.`) ?? false);
33
+ }
34
+ export function removeTable(source, table) {
35
+ const kept = splitSections(source).filter((section) => !ownsHeader(section.header, table));
36
+ return joinSections(kept);
37
+ }
38
+ export function upsertTable(source, table, body) {
39
+ const withoutTable = removeTable(source, table).replace(/\s+$/, "");
40
+ const block = `[${table}]\n${body.trim()}`;
41
+ return withoutTable === "" ? `${block}\n` : `${withoutTable}\n\n${block}\n`;
42
+ }
43
+ export function hasRootKey(source, key) {
44
+ const preamble = splitSections(source)[0];
45
+ if (!preamble)
46
+ return false;
47
+ const pattern = new RegExp(`^\\s*${key}\\s*=`);
48
+ return preamble.lines.some((line) => pattern.test(line));
49
+ }
50
+ export function ensureRootKey(source, key, value) {
51
+ if (hasRootKey(source, key))
52
+ return source;
53
+ const sections = splitSections(source);
54
+ const preamble = sections[0];
55
+ if (!preamble)
56
+ return `${key} = ${value}\n`;
57
+ const assignment = `${key} = ${value}`;
58
+ const body = preamble.lines.join("\n").replace(/^\n+/, "");
59
+ const rest = sections.slice(1);
60
+ const merged = {
61
+ header: undefined,
62
+ lines: body.trim() === "" ? [assignment, ""] : [assignment, "", ...body.split("\n")],
63
+ };
64
+ return joinSections([merged, ...rest]);
65
+ }
66
+ //# sourceMappingURL=toml-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toml-section.js","sourceRoot":"","sources":["../src/toml-section.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export declare const KNOWN_TOKENS: Set<string>;
2
+ export declare const INSTRUCTIONS_BUDGET_CHARS = 1400;
3
+ export declare function findUnknownTokens(text: string): string[];
4
+ export interface BudgetResult {
5
+ readonly ok: boolean;
6
+ readonly length: number;
7
+ }
8
+ export declare function checkBudget(text: string, budget: number): BudgetResult;
9
+ export declare function collectContentFiles(root: string): string[];
10
+ export declare function validateRepository(root: string): string[];
11
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,aAA4C,CAAC;AACtE,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAI9C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAOxD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAEtE;AAUD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BzD"}
@@ -0,0 +1,58 @@
1
+ import { readFileSync, readdirSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export const KNOWN_TOKENS = new Set(["BRAND", "MCP_URL", "DOCS_URL"]);
4
+ export const INSTRUCTIONS_BUDGET_CHARS = 1400;
5
+ const TOKEN_RE = /\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g;
6
+ export function findUnknownTokens(text) {
7
+ const unknown = new Set();
8
+ for (const match of text.matchAll(TOKEN_RE)) {
9
+ const token = match[1];
10
+ if (token !== undefined && !KNOWN_TOKENS.has(token))
11
+ unknown.add(token);
12
+ }
13
+ return [...unknown];
14
+ }
15
+ export function checkBudget(text, budget) {
16
+ return { ok: text.length <= budget, length: text.length };
17
+ }
18
+ function listDir(root, dir, suffix) {
19
+ const target = join(root, dir);
20
+ if (!existsSync(target))
21
+ return [];
22
+ return readdirSync(target)
23
+ .filter((name) => (suffix ? name.endsWith(suffix) : true))
24
+ .map((name) => join(target, name));
25
+ }
26
+ export function collectContentFiles(root) {
27
+ return [
28
+ ...listDir(root, "content", ".md"),
29
+ ...readdirSync(join(root, "skills"), { withFileTypes: true })
30
+ .filter((e) => e.isDirectory())
31
+ .map((e) => join(root, "skills", e.name, "SKILL.md")),
32
+ ...listDir(root, "templates"),
33
+ ].filter(existsSync);
34
+ }
35
+ export function validateRepository(root) {
36
+ const errors = [];
37
+ const instructions = join(root, "content", "instructions.md");
38
+ if (!existsSync(instructions)) {
39
+ errors.push("missing required file: content/instructions.md");
40
+ }
41
+ else {
42
+ const budget = checkBudget(readFileSync(instructions, "utf8"), INSTRUCTIONS_BUDGET_CHARS);
43
+ if (!budget.ok) {
44
+ errors.push(`content/instructions.md is ${budget.length} characters, over the ` +
45
+ `${INSTRUCTIONS_BUDGET_CHARS} character budget. This text is loaded into every ` +
46
+ `session of every connected client, so shorten it or move detail into a skill.`);
47
+ }
48
+ }
49
+ for (const file of collectContentFiles(root)) {
50
+ const unknown = findUnknownTokens(readFileSync(file, "utf8"));
51
+ if (unknown.length) {
52
+ errors.push(`${file.slice(root.length + 1)} uses unknown template tokens: ${unknown.join(", ")}. ` +
53
+ `Known tokens are ${[...KNOWN_TOKENS].join(", ")}.`);
54
+ }
55
+ }
56
+ return errors;
57
+ }
58
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
package/hooks/hooks.json CHANGED
@@ -6,19 +6,32 @@
6
6
  {
7
7
  "type": "command",
8
8
  "command": "node",
9
- "args": ["${CLAUDE_PLUGIN_ROOT}/hooks/scripts/session-start.mjs"],
9
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/hooks/session-start.js"],
10
10
  "timeout": 10
11
11
  }
12
12
  ]
13
13
  }
14
14
  ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node",
21
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/hooks/user-prompt-submit.js"],
22
+ "timeout": 15,
23
+ "statusMessage": "Loading project memory"
24
+ }
25
+ ]
26
+ }
27
+ ],
15
28
  "Stop": [
16
29
  {
17
30
  "hooks": [
18
31
  {
19
32
  "type": "command",
20
33
  "command": "node",
21
- "args": ["${CLAUDE_PLUGIN_ROOT}/hooks/scripts/stop.mjs"],
34
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/hooks/stop.js"],
22
35
  "timeout": 10
23
36
  }
24
37
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jitera/connect",
3
- "version": "0.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Connect AI coding assistants to Jitera projects: documentation, source, and shared project memory",
6
6
  "license": "Apache-2.0",
@@ -20,11 +20,11 @@
20
20
  "node": ">=20"
21
21
  },
22
22
  "bin": {
23
- "jitera-connect": "./bin/jitera-connect.mjs"
23
+ "jitera-connect": "./dist/bin/cli.js",
24
+ "jitera-connect-login": "./dist/bin/login.js"
24
25
  },
25
26
  "files": [
26
- "bin/",
27
- "src/",
27
+ "dist/",
28
28
  "skills/",
29
29
  "hooks/",
30
30
  "content/",
@@ -36,7 +36,14 @@
36
36
  "access": "public"
37
37
  },
38
38
  "scripts": {
39
- "test": "node --test \"test/**/*.test.mjs\"",
40
- "validate": "node scripts/validate.mjs"
39
+ "build": "tsc",
40
+ "typecheck": "tsc --noEmit --pretty false -p tsconfig.test.json",
41
+ "test": "node --test \"test/**/*.test.ts\"",
42
+ "validate": "node src/bin/validate.ts",
43
+ "prepublishOnly": "npm run build"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^26.1.2",
47
+ "typescript": "^7.0.2"
41
48
  }
42
49
  }
@@ -1,120 +0,0 @@
1
- #!/usr/bin/env node
2
- import { homedir } from "node:os";
3
-
4
- import { cursor } from "../src/adapters/cursor.mjs";
5
- import { MalformedConfigError } from "../src/mcp-config.mjs";
6
- import { UnknownEnvironmentError, resolveApiBaseUrl, resolveMcpUrl } from "../src/environments.mjs";
7
-
8
- const ADAPTERS = [cursor];
9
-
10
- function parseArgs(argv) {
11
- const args = { scope: "project" };
12
- for (const arg of argv) {
13
- if (arg.startsWith("--env=")) args.env = arg.slice("--env=".length);
14
- else if (arg === "--dry-run") args.dryRun = true;
15
- else if (arg === "--uninstall") args.uninstall = true;
16
- else if (arg === "--user") args.scope = "user";
17
- else if (arg === "--project") args.scope = "project";
18
- else if (arg === "--print") args.print = true;
19
- else if (arg === "--help" || arg === "-h") args.help = true;
20
- else args.unknown = arg;
21
- }
22
- return args;
23
- }
24
-
25
- function usage() {
26
- return [
27
- "usage: npx @jitera/connect [--env=<environment>] [options]",
28
- "",
29
- " --env=studio-stage staging",
30
- " --env=studio-06 numbered pilot",
31
- " omit --env for production",
32
- "",
33
- " --user write user scoped config instead of project scoped",
34
- " --dry-run report what would change without writing",
35
- " --uninstall remove the jitera server",
36
- " --print print resolved endpoints and exit",
37
- ].join("\n");
38
- }
39
-
40
- function main() {
41
- const args = parseArgs(process.argv.slice(2));
42
- const out = process.stdout;
43
-
44
- if (args.help) {
45
- out.write(`${usage()}\n`);
46
- return;
47
- }
48
-
49
- if (args.unknown) {
50
- process.stderr.write(`error: unrecognised argument "${args.unknown}"\n${usage()}\n`);
51
- process.exitCode = 2;
52
- return;
53
- }
54
-
55
- let mcpUrl;
56
- let apiBaseUrl;
57
- try {
58
- mcpUrl = resolveMcpUrl(args.env);
59
- apiBaseUrl = resolveApiBaseUrl(args.env);
60
- } catch (error) {
61
- if (error instanceof UnknownEnvironmentError) {
62
- process.stderr.write(`error: ${error.message}\n`);
63
- process.exitCode = 2;
64
- return;
65
- }
66
- throw error;
67
- }
68
-
69
- if (args.print) {
70
- out.write(`${JSON.stringify({ mcpUrl, apiBaseUrl }, undefined, 2)}\n`);
71
- return;
72
- }
73
-
74
- const context = { scope: args.scope, home: homedir(), cwd: process.cwd(), mcpUrl, dryRun: args.dryRun };
75
- const detected = ADAPTERS.filter((adapter) => adapter.detect(context));
76
-
77
- if (detected.length === 0) {
78
- process.stderr.write(
79
- `error: no supported assistant detected. Looked for: ${ADAPTERS.map((a) => a.label).join(", ")}.\n` +
80
- `Claude Code and Codex install through their own plugin marketplaces, see the readme.\n`
81
- );
82
- process.exitCode = 1;
83
- return;
84
- }
85
-
86
- const results = [];
87
- for (const adapter of detected) {
88
- try {
89
- results.push({
90
- adapter,
91
- result: args.uninstall ? adapter.uninstall(context) : adapter.install(context),
92
- });
93
- } catch (error) {
94
- if (error instanceof MalformedConfigError) {
95
- process.stderr.write(`error: ${error.message}\n`);
96
- process.exitCode = 1;
97
- return;
98
- }
99
- throw error;
100
- }
101
- }
102
-
103
- const verb = args.uninstall ? "removed from" : "written to";
104
- for (const { adapter, result } of results) {
105
- const state = result.changed ? verb : "already up to date in";
106
- out.write(`${adapter.label}: ${state} ${result.path}\n`);
107
- }
108
-
109
- if (args.dryRun) {
110
- out.write("\ndry run, nothing was written\n");
111
- return;
112
- }
113
-
114
- if (!args.uninstall) {
115
- out.write(`\nendpoint ${mcpUrl}\n`);
116
- out.write("export JITERA_API_KEY=<your api key> before starting your assistant\n");
117
- }
118
- }
119
-
120
- main();
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
3
- import { join, dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
7
-
8
- function drainStdin() {
9
- try {
10
- readFileSync(0, "utf8");
11
- } catch {
12
- return;
13
- }
14
- }
15
-
16
- function main() {
17
- drainStdin();
18
-
19
- let directive = "";
20
- try {
21
- directive = readFileSync(join(ROOT, "content", "session-start.md"), "utf8").trim();
22
- } catch {
23
- process.exit(0);
24
- }
25
-
26
- process.stdout.write(
27
- JSON.stringify({
28
- hookSpecificOutput: {
29
- hookEventName: "SessionStart",
30
- additionalContext: directive,
31
- },
32
- })
33
- );
34
- }
35
-
36
- main();
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
3
- import { join, dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
7
- const CHECKPOINT_EVERY_TURNS = 5;
8
-
9
- function readInput() {
10
- try {
11
- return JSON.parse(readFileSync(0, "utf8"));
12
- } catch {
13
- return {};
14
- }
15
- }
16
-
17
- function main() {
18
- const turn = readInput().turn_number;
19
- if (!Number.isInteger(turn) || turn < 1) process.exit(0);
20
- if (turn % CHECKPOINT_EVERY_TURNS !== 0) process.exit(0);
21
-
22
- let directive = "";
23
- try {
24
- directive = readFileSync(join(ROOT, "content", "checkpoint.md"), "utf8").trim();
25
- } catch {
26
- process.exit(0);
27
- }
28
-
29
- process.stdout.write(
30
- JSON.stringify({
31
- hookSpecificOutput: {
32
- hookEventName: "Stop",
33
- additionalContext: directive,
34
- },
35
- })
36
- );
37
- }
38
-
39
- main();