@kodrunhq/claudefy 0.1.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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/dist/backup-manager/backup-manager.d.ts +6 -0
  4. package/dist/backup-manager/backup-manager.js +27 -0
  5. package/dist/backup-manager/backup-manager.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +296 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/config.d.ts +8 -0
  10. package/dist/commands/config.js +56 -0
  11. package/dist/commands/config.js.map +1 -0
  12. package/dist/commands/doctor.d.ts +14 -0
  13. package/dist/commands/doctor.js +64 -0
  14. package/dist/commands/doctor.js.map +1 -0
  15. package/dist/commands/hooks.d.ts +7 -0
  16. package/dist/commands/hooks.js +18 -0
  17. package/dist/commands/hooks.js.map +1 -0
  18. package/dist/commands/init.d.ts +13 -0
  19. package/dist/commands/init.js +66 -0
  20. package/dist/commands/init.js.map +1 -0
  21. package/dist/commands/join.d.ts +12 -0
  22. package/dist/commands/join.js +51 -0
  23. package/dist/commands/join.js.map +1 -0
  24. package/dist/commands/link.d.ts +12 -0
  25. package/dist/commands/link.js +34 -0
  26. package/dist/commands/link.js.map +1 -0
  27. package/dist/commands/machines.d.ts +6 -0
  28. package/dist/commands/machines.js +25 -0
  29. package/dist/commands/machines.js.map +1 -0
  30. package/dist/commands/override.d.ts +12 -0
  31. package/dist/commands/override.js +44 -0
  32. package/dist/commands/override.js.map +1 -0
  33. package/dist/commands/pull.d.ts +17 -0
  34. package/dist/commands/pull.js +220 -0
  35. package/dist/commands/pull.js.map +1 -0
  36. package/dist/commands/push.d.ts +14 -0
  37. package/dist/commands/push.js +175 -0
  38. package/dist/commands/push.js.map +1 -0
  39. package/dist/commands/status.d.ts +14 -0
  40. package/dist/commands/status.js +50 -0
  41. package/dist/commands/status.js.map +1 -0
  42. package/dist/config/config-manager.d.ts +22 -0
  43. package/dist/config/config-manager.js +118 -0
  44. package/dist/config/config-manager.js.map +1 -0
  45. package/dist/config/defaults.d.ts +7 -0
  46. package/dist/config/defaults.js +33 -0
  47. package/dist/config/defaults.js.map +1 -0
  48. package/dist/config/types.d.ts +25 -0
  49. package/dist/config/types.js +2 -0
  50. package/dist/config/types.js.map +1 -0
  51. package/dist/encryptor/encryptor.d.ts +10 -0
  52. package/dist/encryptor/encryptor.js +68 -0
  53. package/dist/encryptor/encryptor.js.map +1 -0
  54. package/dist/encryptor/passphrase.d.ts +7 -0
  55. package/dist/encryptor/passphrase.js +34 -0
  56. package/dist/encryptor/passphrase.js.map +1 -0
  57. package/dist/git-adapter/git-adapter.d.ts +19 -0
  58. package/dist/git-adapter/git-adapter.js +104 -0
  59. package/dist/git-adapter/git-adapter.js.map +1 -0
  60. package/dist/git-adapter/types.d.ts +14 -0
  61. package/dist/git-adapter/types.js +2 -0
  62. package/dist/git-adapter/types.js.map +1 -0
  63. package/dist/hook-manager/hook-manager.d.ts +11 -0
  64. package/dist/hook-manager/hook-manager.js +110 -0
  65. package/dist/hook-manager/hook-manager.js.map +1 -0
  66. package/dist/index.d.ts +2 -0
  67. package/dist/index.js +4 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/machine-registry/machine-registry.d.ts +16 -0
  70. package/dist/machine-registry/machine-registry.js +65 -0
  71. package/dist/machine-registry/machine-registry.js.map +1 -0
  72. package/dist/merger/merger.d.ts +10 -0
  73. package/dist/merger/merger.js +15 -0
  74. package/dist/merger/merger.js.map +1 -0
  75. package/dist/output.d.ts +8 -0
  76. package/dist/output.js +10 -0
  77. package/dist/output.js.map +1 -0
  78. package/dist/path-mapper/git-identity.d.ts +8 -0
  79. package/dist/path-mapper/git-identity.js +35 -0
  80. package/dist/path-mapper/git-identity.js.map +1 -0
  81. package/dist/path-mapper/path-mapper.d.ts +19 -0
  82. package/dist/path-mapper/path-mapper.js +101 -0
  83. package/dist/path-mapper/path-mapper.js.map +1 -0
  84. package/dist/repo-creator/repo-creator.d.ts +6 -0
  85. package/dist/repo-creator/repo-creator.js +52 -0
  86. package/dist/repo-creator/repo-creator.js.map +1 -0
  87. package/dist/secret-scanner/scanner.d.ts +10 -0
  88. package/dist/secret-scanner/scanner.js +48 -0
  89. package/dist/secret-scanner/scanner.js.map +1 -0
  90. package/dist/sync-filter/sync-filter.d.ts +8 -0
  91. package/dist/sync-filter/sync-filter.js +37 -0
  92. package/dist/sync-filter/sync-filter.js.map +1 -0
  93. package/dist/sync-filter/types.d.ts +13 -0
  94. package/dist/sync-filter/types.js +2 -0
  95. package/dist/sync-filter/types.js.map +1 -0
  96. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.js","sourceRoot":"","sources":["../../src/merger/merger.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,MAAM,OAAO,MAAM;IACjB,aAAa,CAAC,KAA0B,EAAE,MAA2B;QACnE,OAAO,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE;YAC9B,8DAA8D;YAC9D,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM;SACxC,CAAC,CAAC;IACL,CAAC;IAED,aAAa,CACX,KAAyC,EACzC,MAA0C;QAE1C,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC;QACrD,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export declare const output: {
2
+ success: (msg: string) => void;
3
+ info: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ dim: (msg: string) => void;
7
+ heading: (msg: string) => void;
8
+ };
package/dist/output.js ADDED
@@ -0,0 +1,10 @@
1
+ import chalk from "chalk";
2
+ export const output = {
3
+ success: (msg) => console.log(chalk.green("\u2714") + " " + msg),
4
+ info: (msg) => console.log(chalk.blue("\u2139") + " " + msg),
5
+ warn: (msg) => console.log(chalk.yellow("\u26A0") + " " + msg),
6
+ error: (msg) => console.error(chalk.red("\u2716") + " " + msg),
7
+ dim: (msg) => console.log(chalk.dim(msg)),
8
+ heading: (msg) => console.log(chalk.bold.underline(msg)),
9
+ };
10
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;IACxE,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;IACpE,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;IACtE,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;IACtE,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;CACjE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface GitIdentityResult {
2
+ canonicalId: string;
3
+ gitRemote: string;
4
+ }
5
+ export declare class GitIdentity {
6
+ detect(dirPath: string): Promise<GitIdentityResult | null>;
7
+ urlToCanonicalId(url: string): string;
8
+ }
@@ -0,0 +1,35 @@
1
+ import { simpleGit } from "simple-git";
2
+ export class GitIdentity {
3
+ async detect(dirPath) {
4
+ try {
5
+ const git = simpleGit(dirPath);
6
+ const isRepo = await git.checkIsRepo();
7
+ if (!isRepo)
8
+ return null;
9
+ const remotes = await git.getRemotes(true);
10
+ const origin = remotes.find((r) => r.name === "origin");
11
+ if (!origin?.refs?.fetch)
12
+ return null;
13
+ const remoteUrl = origin.refs.fetch;
14
+ const canonicalId = this.urlToCanonicalId(remoteUrl);
15
+ return { canonicalId, gitRemote: remoteUrl };
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ urlToCanonicalId(url) {
22
+ let normalized = url.toLowerCase();
23
+ normalized = normalized.replace(/\.git$/, "");
24
+ const sshMatch = normalized.match(/^git@([^:]+):(.+)$/);
25
+ if (sshMatch) {
26
+ return `${sshMatch[1]}--${sshMatch[2].replace(/\//g, "--")}`;
27
+ }
28
+ const httpsMatch = normalized.match(/^https?:\/\/([^/]+)\/(.+)$/);
29
+ if (httpsMatch) {
30
+ return `${httpsMatch[1]}--${httpsMatch[2].replace(/\//g, "--")}`;
31
+ }
32
+ return normalized.replace(/[/:@]/g, "--");
33
+ }
34
+ }
35
+ //# sourceMappingURL=git-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-identity.js","sourceRoot":"","sources":["../../src/path-mapper/git-identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAOvC,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YAEzB,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YACxD,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK;gBAAE,OAAO,IAAI,CAAC;YAEtC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YACpC,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAErD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,GAAW;QAC1B,IAAI,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAE9C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxD,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QAC/D,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAClE,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QACnE,CAAC;QAED,OAAO,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { LinksConfig } from "../config/types.js";
2
+ export declare class PathMapper {
3
+ private links;
4
+ private canonicalToAlias;
5
+ private dirNameToCanonical;
6
+ constructor(links: LinksConfig);
7
+ normalizeDirName(dirName: string): string | null;
8
+ remapDirName(canonicalId: string): string | null;
9
+ normalizeJsonlLine(line: string): string;
10
+ remapJsonlLine(line: string): string;
11
+ normalizeSettingsPaths<T>(settings: T, claudeDir: string): T;
12
+ remapSettingsPaths<T>(settings: T, claudeDir: string): T;
13
+ normalizePluginPaths<T>(plugins: T, claudeDir: string): T;
14
+ remapPluginPaths<T>(plugins: T, claudeDir: string): T;
15
+ private replaceInSerialized;
16
+ private pathToDirName;
17
+ private normalizePathField;
18
+ private remapPathField;
19
+ }
@@ -0,0 +1,101 @@
1
+ const CLAUDE_DIR_SENTINEL = "@@CLAUDE_DIR@@";
2
+ export class PathMapper {
3
+ links;
4
+ canonicalToAlias;
5
+ dirNameToCanonical;
6
+ constructor(links) {
7
+ this.links = links;
8
+ this.canonicalToAlias = new Map();
9
+ this.dirNameToCanonical = new Map();
10
+ for (const [alias, info] of Object.entries(links)) {
11
+ // Normalize trailing slash from localPath
12
+ const normalizedPath = info.localPath.replace(/\/+$/, "");
13
+ this.canonicalToAlias.set(info.canonicalId, alias);
14
+ // Pre-compute the encoded dir name for each link
15
+ const encoded = this.pathToDirName(normalizedPath);
16
+ this.dirNameToCanonical.set(encoded, info.canonicalId);
17
+ }
18
+ }
19
+ normalizeDirName(dirName) {
20
+ return this.dirNameToCanonical.get(dirName) ?? null;
21
+ }
22
+ remapDirName(canonicalId) {
23
+ const alias = this.canonicalToAlias.get(canonicalId);
24
+ if (!alias)
25
+ return null;
26
+ return this.pathToDirName(this.links[alias].localPath);
27
+ }
28
+ normalizeJsonlLine(line) {
29
+ try {
30
+ const obj = JSON.parse(line);
31
+ if (obj.project) {
32
+ obj.project = this.normalizePathField(obj.project);
33
+ }
34
+ if (obj.cwd) {
35
+ obj.cwd = this.normalizePathField(obj.cwd);
36
+ }
37
+ return JSON.stringify(obj);
38
+ }
39
+ catch {
40
+ return line;
41
+ }
42
+ }
43
+ remapJsonlLine(line) {
44
+ try {
45
+ const obj = JSON.parse(line);
46
+ if (obj.project) {
47
+ obj.project = this.remapPathField(obj.project);
48
+ }
49
+ if (obj.cwd) {
50
+ obj.cwd = this.remapPathField(obj.cwd);
51
+ }
52
+ return JSON.stringify(obj);
53
+ }
54
+ catch {
55
+ return line;
56
+ }
57
+ }
58
+ normalizeSettingsPaths(settings, claudeDir) {
59
+ return this.replaceInSerialized(settings, claudeDir, CLAUDE_DIR_SENTINEL);
60
+ }
61
+ remapSettingsPaths(settings, claudeDir) {
62
+ return this.replaceInSerialized(settings, CLAUDE_DIR_SENTINEL, claudeDir);
63
+ }
64
+ normalizePluginPaths(plugins, claudeDir) {
65
+ return this.replaceInSerialized(plugins, claudeDir, CLAUDE_DIR_SENTINEL);
66
+ }
67
+ remapPluginPaths(plugins, claudeDir) {
68
+ return this.replaceInSerialized(plugins, CLAUDE_DIR_SENTINEL, claudeDir);
69
+ }
70
+ replaceInSerialized(value, search, replacement) {
71
+ const json = JSON.stringify(value);
72
+ const updated = json.replaceAll(search, replacement);
73
+ return JSON.parse(updated);
74
+ }
75
+ pathToDirName(localPath) {
76
+ // Claude Code encodes paths by replacing / with -
77
+ // e.g. /home/user/project -> -home-user-project
78
+ return localPath.replace(/\//g, "-");
79
+ }
80
+ normalizePathField(value) {
81
+ for (const [alias, info] of Object.entries(this.links)) {
82
+ const localPath = info.localPath.replace(/\/+$/, "");
83
+ if (value === localPath || value.startsWith(localPath + "/")) {
84
+ return `@@${alias}@@${value.slice(localPath.length)}`;
85
+ }
86
+ }
87
+ return value;
88
+ }
89
+ remapPathField(value) {
90
+ const match = value.match(/^@@([^@]+)@@(.*)$/);
91
+ if (!match)
92
+ return value;
93
+ const alias = match[1];
94
+ const suffix = match[2];
95
+ const info = this.links[alias];
96
+ if (!info)
97
+ return value;
98
+ return info.localPath.replace(/\/+$/, "") + suffix;
99
+ }
100
+ }
101
+ //# sourceMappingURL=path-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-mapper.js","sourceRoot":"","sources":["../../src/path-mapper/path-mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type RepoProvider = "github" | "gitlab";
2
+ export declare class RepoCreator {
3
+ detect(): Promise<RepoProvider | null>;
4
+ create(name: string, provider?: RepoProvider): Promise<string>;
5
+ private isAvailable;
6
+ }
@@ -0,0 +1,52 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const execFileAsync = promisify(execFile);
4
+ export class RepoCreator {
5
+ async detect() {
6
+ if (await this.isAvailable("gh"))
7
+ return "github";
8
+ if (await this.isAvailable("glab"))
9
+ return "gitlab";
10
+ return null;
11
+ }
12
+ async create(name, provider) {
13
+ const detected = provider || (await this.detect());
14
+ if (!detected) {
15
+ throw new Error("No supported CLI found. Install 'gh' (GitHub) or 'glab' (GitLab).");
16
+ }
17
+ if (detected === "github") {
18
+ const { stdout } = await execFileAsync("gh", ["repo", "create", name, "--private", "--yes"]);
19
+ // gh repo create outputs the URL
20
+ const url = stdout.trim().split("\n").pop()?.trim();
21
+ if (!url)
22
+ throw new Error("Failed to parse repo URL from gh output");
23
+ return url;
24
+ }
25
+ if (detected === "gitlab") {
26
+ const { stdout } = await execFileAsync("glab", [
27
+ "project",
28
+ "create",
29
+ "--name",
30
+ name,
31
+ "--visibility",
32
+ "private",
33
+ ]);
34
+ const urlMatch = stdout.match(/https?:\/\/\S+/);
35
+ if (!urlMatch)
36
+ throw new Error("Failed to parse repo URL from glab output");
37
+ return urlMatch[0];
38
+ }
39
+ throw new Error(`Unsupported provider: ${detected}`);
40
+ }
41
+ async isAvailable(cmd) {
42
+ try {
43
+ const whichCmd = process.platform === "win32" ? "where" : "which";
44
+ await execFileAsync(whichCmd, [cmd]);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=repo-creator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-creator.js","sourceRoot":"","sources":["../../src/repo-creator/repo-creator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAI1C,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,MAAM;QACV,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC;QAClD,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YAAE,OAAO,QAAQ,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,QAAuB;QAChD,MAAM,QAAQ,GAAG,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACvF,CAAC;QAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7F,iCAAiC;YACjC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;YACpD,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACrE,OAAO,GAAG,CAAC;QACb,CAAC;QAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE;gBAC7C,SAAS;gBACT,QAAQ;gBACR,QAAQ;gBACR,IAAI;gBACJ,cAAc;gBACd,SAAS;aACV,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC5E,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW;QACnC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YAClE,MAAM,aAAa,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ export interface SecretFinding {
2
+ file: string;
3
+ line: number;
4
+ pattern: string;
5
+ snippet: string;
6
+ }
7
+ export declare class SecretScanner {
8
+ scanFile(filePath: string): Promise<SecretFinding[]>;
9
+ scanFiles(filePaths: string[]): Promise<SecretFinding[]>;
10
+ }
@@ -0,0 +1,48 @@
1
+ import { readFile } from "node:fs/promises";
2
+ const SECRET_PATTERNS = [
3
+ { name: "Anthropic API Key", regex: /sk-ant-[a-zA-Z0-9_-]{20,}/ },
4
+ { name: "OpenAI API Key", regex: /sk-[a-zA-Z0-9]{20,}/ },
5
+ { name: "AWS Access Key", regex: /AKIA[0-9A-Z]{16}/ },
6
+ { name: "GitHub Token", regex: /ghp_[A-Za-z0-9]{36}/ },
7
+ { name: "GitHub OAuth", regex: /gho_[A-Za-z0-9]{36}/ },
8
+ { name: "GitLab Token", regex: /glpat-[A-Za-z0-9\-_]{20,}/ },
9
+ { name: "Generic Bearer", regex: /Bearer\s+[a-zA-Z0-9_\-.]{20,}/ },
10
+ {
11
+ name: "Generic Secret Key",
12
+ regex: /"(?:secret|password|token|apiKey|api_key|private_key)"\s*:\s*"[^"]{8,}"/,
13
+ },
14
+ { name: "High Entropy Base64", regex: /[A-Za-z0-9+/]{40,}={0,2}/ },
15
+ ];
16
+ export class SecretScanner {
17
+ async scanFile(filePath) {
18
+ const content = await readFile(filePath, "utf-8");
19
+ const findings = [];
20
+ const lines = content.split("\n");
21
+ for (let i = 0; i < lines.length; i++) {
22
+ for (const pattern of SECRET_PATTERNS) {
23
+ const match = pattern.regex.exec(lines[i]);
24
+ if (match) {
25
+ // Redact matched secret, showing only prefix and suffix
26
+ const matched = match[0];
27
+ const redacted = matched.length > 8 ? matched.slice(0, 4) + "****" + matched.slice(-4) : "****";
28
+ findings.push({
29
+ file: filePath,
30
+ line: i + 1,
31
+ pattern: pattern.name,
32
+ snippet: lines[i].slice(0, 80).replace(matched, redacted),
33
+ });
34
+ }
35
+ }
36
+ }
37
+ return findings;
38
+ }
39
+ async scanFiles(filePaths) {
40
+ const results = [];
41
+ for (const filePath of filePaths) {
42
+ const findings = await this.scanFile(filePath);
43
+ results.push(...findings);
44
+ }
45
+ return results;
46
+ }
47
+ }
48
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/secret-scanner/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAS5C,MAAM,eAAe,GAAsC;IACzD,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,2BAA2B,EAAE;IACjE,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACxD,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACrD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACtD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,qBAAqB,EAAE;IACtD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,2BAA2B,EAAE;IAC5D,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,+BAA+B,EAAE;IAClE;QACE,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,yEAAyE;KACjF;IACD,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,0BAA0B,EAAE;CACnE,CAAC;AAEF,MAAM,OAAO,aAAa;IACxB,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC7B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAoB,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3C,IAAI,KAAK,EAAE,CAAC;oBACV,wDAAwD;oBACxD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACzB,MAAM,QAAQ,GACZ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;oBACjF,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,OAAO,EAAE,OAAO,CAAC,IAAI;wBACrB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC;qBAC1D,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,SAAmB;QACjC,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { SyncFilterConfig } from "../config/types.js";
2
+ import type { SyncTier, ClassificationResult } from "./types.js";
3
+ export declare class SyncFilter {
4
+ private config;
5
+ constructor(config: SyncFilterConfig);
6
+ classify(claudeDir: string): Promise<ClassificationResult>;
7
+ getTier(name: string): SyncTier;
8
+ }
@@ -0,0 +1,37 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ export class SyncFilter {
4
+ config;
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ async classify(claudeDir) {
9
+ const entries = await readdir(claudeDir, { withFileTypes: true });
10
+ const items = [];
11
+ for (const entry of entries) {
12
+ const tier = this.getTier(entry.name);
13
+ const fullPath = join(claudeDir, entry.name);
14
+ const stats = await stat(fullPath);
15
+ items.push({
16
+ name: entry.name,
17
+ tier,
18
+ isDirectory: entry.isDirectory(),
19
+ sizeBytes: stats.size,
20
+ });
21
+ }
22
+ return {
23
+ items,
24
+ allowlist: items.filter((i) => i.tier === "allow"),
25
+ denylist: items.filter((i) => i.tier === "deny"),
26
+ unknown: items.filter((i) => i.tier === "unknown"),
27
+ };
28
+ }
29
+ getTier(name) {
30
+ if (this.config.allowlist.includes(name))
31
+ return "allow";
32
+ if (this.config.denylist.includes(name))
33
+ return "deny";
34
+ return "unknown";
35
+ }
36
+ }
37
+ //# sourceMappingURL=sync-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-filter.js","sourceRoot":"","sources":["../../src/sync-filter/sync-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,OAAO,UAAU;IACb,MAAM,CAAmB;IAEjC,YAAY,MAAwB;QAClC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAiB;QAC9B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,KAAK,GAAqB,EAAE,CAAC;QAEnC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEnC,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI;gBACJ,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE;gBAChC,SAAS,EAAE,KAAK,CAAC,IAAI;aACtB,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,KAAK;YACL,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC;YAClD,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;YAChD,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,OAAO,CAAC;QACzD,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ export type SyncTier = "allow" | "deny" | "unknown";
2
+ export interface ClassifiedItem {
3
+ name: string;
4
+ tier: SyncTier;
5
+ isDirectory: boolean;
6
+ sizeBytes: number;
7
+ }
8
+ export interface ClassificationResult {
9
+ items: ClassifiedItem[];
10
+ allowlist: ClassifiedItem[];
11
+ denylist: ClassifiedItem[];
12
+ unknown: ClassifiedItem[];
13
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync-filter/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@kodrunhq/claudefy",
3
+ "version": "0.1.0",
4
+ "description": "Sync your Claude Code environment across machines",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "claudefy": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx src/index.ts",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "lint": "eslint src/ tests/",
16
+ "lint:fix": "eslint src/ tests/ --fix",
17
+ "format": "prettier --write src/ tests/",
18
+ "format:check": "prettier --check src/ tests/"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/kodrunhq/claudefy.git"
23
+ },
24
+ "keywords": [
25
+ "claude",
26
+ "claude-code",
27
+ "sync",
28
+ "cli",
29
+ "dotfiles"
30
+ ],
31
+ "author": "Kodrun",
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "license": "MIT",
41
+ "bugs": {
42
+ "url": "https://github.com/kodrunhq/claudefy/issues"
43
+ },
44
+ "homepage": "https://github.com/kodrunhq/claudefy#readme",
45
+ "dependencies": {
46
+ "age-encryption": "^0.3.0",
47
+ "chalk": "^5.6.2",
48
+ "commander": "^14.0.3",
49
+ "deepmerge": "^4.3.1",
50
+ "simple-git": "^3.32.3"
51
+ },
52
+ "peerDependencies": {
53
+ "keytar": ">=7.0.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "keytar": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@eslint/js": "^10.0.1",
62
+ "@types/node": "^25.4.0",
63
+ "@typescript-eslint/eslint-plugin": "^8.57.0",
64
+ "@typescript-eslint/parser": "^8.57.0",
65
+ "eslint": "^10.0.3",
66
+ "prettier": "^3.8.1",
67
+ "tsx": "^4.21.0",
68
+ "typescript": "^5.9.3",
69
+ "typescript-eslint": "^8.57.0",
70
+ "vitest": "^4.0.18"
71
+ }
72
+ }