@phren/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +590 -0
  3. package/mcp/dist/capabilities/cli.js +61 -0
  4. package/mcp/dist/capabilities/index.js +15 -0
  5. package/mcp/dist/capabilities/mcp.js +61 -0
  6. package/mcp/dist/capabilities/types.js +57 -0
  7. package/mcp/dist/capabilities/vscode.js +61 -0
  8. package/mcp/dist/capabilities/web-ui.js +61 -0
  9. package/mcp/dist/cli-actions.js +302 -0
  10. package/mcp/dist/cli-config.js +580 -0
  11. package/mcp/dist/cli-extract.js +305 -0
  12. package/mcp/dist/cli-govern.js +371 -0
  13. package/mcp/dist/cli-graph.js +169 -0
  14. package/mcp/dist/cli-hooks-citations.js +44 -0
  15. package/mcp/dist/cli-hooks-context.js +56 -0
  16. package/mcp/dist/cli-hooks-globs.js +83 -0
  17. package/mcp/dist/cli-hooks-output.js +130 -0
  18. package/mcp/dist/cli-hooks-retrieval.js +2 -0
  19. package/mcp/dist/cli-hooks-session.js +1402 -0
  20. package/mcp/dist/cli-hooks.js +350 -0
  21. package/mcp/dist/cli-namespaces.js +989 -0
  22. package/mcp/dist/cli-ops.js +253 -0
  23. package/mcp/dist/cli-search.js +407 -0
  24. package/mcp/dist/cli.js +108 -0
  25. package/mcp/dist/content-archive.js +278 -0
  26. package/mcp/dist/content-citation.js +391 -0
  27. package/mcp/dist/content-dedup.js +622 -0
  28. package/mcp/dist/content-learning.js +472 -0
  29. package/mcp/dist/content-metadata.js +186 -0
  30. package/mcp/dist/content-validate.js +462 -0
  31. package/mcp/dist/core-finding.js +54 -0
  32. package/mcp/dist/core-project.js +36 -0
  33. package/mcp/dist/core-search.js +50 -0
  34. package/mcp/dist/data-access.js +400 -0
  35. package/mcp/dist/data-tasks.js +821 -0
  36. package/mcp/dist/embedding.js +344 -0
  37. package/mcp/dist/entrypoint.js +387 -0
  38. package/mcp/dist/finding-context.js +172 -0
  39. package/mcp/dist/finding-impact.js +181 -0
  40. package/mcp/dist/finding-journal.js +122 -0
  41. package/mcp/dist/finding-lifecycle.js +259 -0
  42. package/mcp/dist/governance-audit.js +22 -0
  43. package/mcp/dist/governance-locks.js +96 -0
  44. package/mcp/dist/governance-policy.js +648 -0
  45. package/mcp/dist/governance-scores.js +355 -0
  46. package/mcp/dist/hooks.js +449 -0
  47. package/mcp/dist/impact-scoring.js +22 -0
  48. package/mcp/dist/index-query.js +168 -0
  49. package/mcp/dist/index.js +205 -0
  50. package/mcp/dist/init-config.js +336 -0
  51. package/mcp/dist/init-preferences.js +62 -0
  52. package/mcp/dist/init-setup.js +1305 -0
  53. package/mcp/dist/init-shared.js +29 -0
  54. package/mcp/dist/init.js +1730 -0
  55. package/mcp/dist/link-checksums.js +62 -0
  56. package/mcp/dist/link-context.js +257 -0
  57. package/mcp/dist/link-doctor.js +591 -0
  58. package/mcp/dist/link-skills.js +212 -0
  59. package/mcp/dist/link.js +596 -0
  60. package/mcp/dist/logger.js +15 -0
  61. package/mcp/dist/machine-identity.js +38 -0
  62. package/mcp/dist/mcp-config.js +254 -0
  63. package/mcp/dist/mcp-data.js +315 -0
  64. package/mcp/dist/mcp-extract-facts.js +78 -0
  65. package/mcp/dist/mcp-extract.js +133 -0
  66. package/mcp/dist/mcp-finding.js +557 -0
  67. package/mcp/dist/mcp-graph.js +339 -0
  68. package/mcp/dist/mcp-hooks.js +256 -0
  69. package/mcp/dist/mcp-memory.js +58 -0
  70. package/mcp/dist/mcp-ops.js +328 -0
  71. package/mcp/dist/mcp-search.js +628 -0
  72. package/mcp/dist/mcp-session.js +651 -0
  73. package/mcp/dist/mcp-skills.js +189 -0
  74. package/mcp/dist/mcp-tasks.js +551 -0
  75. package/mcp/dist/mcp-types.js +7 -0
  76. package/mcp/dist/memory-ui-assets.js +6 -0
  77. package/mcp/dist/memory-ui-data.js +513 -0
  78. package/mcp/dist/memory-ui-graph.js +1910 -0
  79. package/mcp/dist/memory-ui-page.js +353 -0
  80. package/mcp/dist/memory-ui-scripts.js +1387 -0
  81. package/mcp/dist/memory-ui-server.js +1218 -0
  82. package/mcp/dist/memory-ui-styles.js +555 -0
  83. package/mcp/dist/memory-ui.js +9 -0
  84. package/mcp/dist/package-metadata.js +13 -0
  85. package/mcp/dist/phren-art.js +52 -0
  86. package/mcp/dist/phren-core.js +108 -0
  87. package/mcp/dist/phren-dotenv.js +67 -0
  88. package/mcp/dist/phren-paths.js +476 -0
  89. package/mcp/dist/proactivity.js +172 -0
  90. package/mcp/dist/profile-store.js +228 -0
  91. package/mcp/dist/project-config.js +85 -0
  92. package/mcp/dist/project-locator.js +25 -0
  93. package/mcp/dist/project-topics.js +1134 -0
  94. package/mcp/dist/provider-adapters.js +176 -0
  95. package/mcp/dist/runtime-profile.js +18 -0
  96. package/mcp/dist/session-checkpoints.js +131 -0
  97. package/mcp/dist/session-utils.js +68 -0
  98. package/mcp/dist/shared-content.js +8 -0
  99. package/mcp/dist/shared-embedding-cache.js +143 -0
  100. package/mcp/dist/shared-fragment-graph.js +456 -0
  101. package/mcp/dist/shared-governance.js +4 -0
  102. package/mcp/dist/shared-index.js +1334 -0
  103. package/mcp/dist/shared-ollama.js +192 -0
  104. package/mcp/dist/shared-paths.js +1 -0
  105. package/mcp/dist/shared-retrieval.js +796 -0
  106. package/mcp/dist/shared-search-fallback.js +375 -0
  107. package/mcp/dist/shared-sqljs.js +42 -0
  108. package/mcp/dist/shared-stemmer.js +171 -0
  109. package/mcp/dist/shared-vector-index.js +199 -0
  110. package/mcp/dist/shared.js +114 -0
  111. package/mcp/dist/shell-entry.js +209 -0
  112. package/mcp/dist/shell-input.js +943 -0
  113. package/mcp/dist/shell-palette.js +119 -0
  114. package/mcp/dist/shell-render.js +252 -0
  115. package/mcp/dist/shell-state-store.js +81 -0
  116. package/mcp/dist/shell-types.js +13 -0
  117. package/mcp/dist/shell-view-list.js +14 -0
  118. package/mcp/dist/shell-view.js +707 -0
  119. package/mcp/dist/shell.js +352 -0
  120. package/mcp/dist/skill-files.js +117 -0
  121. package/mcp/dist/skill-registry.js +279 -0
  122. package/mcp/dist/skill-state.js +28 -0
  123. package/mcp/dist/startup-embedding.js +57 -0
  124. package/mcp/dist/status.js +323 -0
  125. package/mcp/dist/synonyms.json +670 -0
  126. package/mcp/dist/task-hygiene.js +251 -0
  127. package/mcp/dist/task-lifecycle.js +347 -0
  128. package/mcp/dist/tasks-github.js +76 -0
  129. package/mcp/dist/telemetry.js +165 -0
  130. package/mcp/dist/test-global-setup.js +37 -0
  131. package/mcp/dist/tool-registry.js +104 -0
  132. package/mcp/dist/update.js +97 -0
  133. package/mcp/dist/utils.js +543 -0
  134. package/package.json +67 -0
  135. package/skills/README.md +7 -0
  136. package/skills/consolidate/SKILL.md +152 -0
  137. package/skills/discover/SKILL.md +175 -0
  138. package/skills/init/SKILL.md +216 -0
  139. package/skills/profiles/SKILL.md +121 -0
  140. package/skills/sync/SKILL.md +261 -0
  141. package/starter/README.md +74 -0
  142. package/starter/global/CLAUDE.md +89 -0
  143. package/starter/global/skills/humanize.md +30 -0
  144. package/starter/global/skills/pipeline.md +35 -0
  145. package/starter/global/skills/release.md +35 -0
  146. package/starter/machines.yaml +8 -0
  147. package/starter/my-api/.claude/skills/README.md +7 -0
  148. package/starter/my-api/CLAUDE.md +33 -0
  149. package/starter/my-api/FINDINGS.md +9 -0
  150. package/starter/my-api/summary.md +7 -0
  151. package/starter/my-api/tasks.md +7 -0
  152. package/starter/my-first-project/.claude/skills/README.md +7 -0
  153. package/starter/my-first-project/CLAUDE.md +49 -0
  154. package/starter/my-first-project/FINDINGS.md +24 -0
  155. package/starter/my-first-project/summary.md +11 -0
  156. package/starter/my-first-project/tasks.md +25 -0
  157. package/starter/my-frontend/.claude/skills/README.md +7 -0
  158. package/starter/my-frontend/CLAUDE.md +33 -0
  159. package/starter/my-frontend/FINDINGS.md +9 -0
  160. package/starter/my-frontend/summary.md +7 -0
  161. package/starter/my-frontend/tasks.md +7 -0
  162. package/starter/profiles/default.yaml +4 -0
  163. package/starter/profiles/personal.yaml +4 -0
  164. package/starter/profiles/work.yaml +4 -0
  165. package/starter/templates/README.md +7 -0
  166. package/starter/templates/frontend/CLAUDE.md +23 -0
  167. package/starter/templates/frontend/FINDINGS.md +7 -0
  168. package/starter/templates/frontend/reference/README.md +4 -0
  169. package/starter/templates/frontend/summary.md +7 -0
  170. package/starter/templates/frontend/tasks.md +11 -0
  171. package/starter/templates/library/CLAUDE.md +22 -0
  172. package/starter/templates/library/FINDINGS.md +7 -0
  173. package/starter/templates/library/reference/README.md +4 -0
  174. package/starter/templates/library/summary.md +7 -0
  175. package/starter/templates/library/tasks.md +11 -0
  176. package/starter/templates/monorepo/CLAUDE.md +21 -0
  177. package/starter/templates/monorepo/FINDINGS.md +7 -0
  178. package/starter/templates/monorepo/reference/README.md +4 -0
  179. package/starter/templates/monorepo/summary.md +7 -0
  180. package/starter/templates/monorepo/tasks.md +11 -0
  181. package/starter/templates/python-project/CLAUDE.md +21 -0
  182. package/starter/templates/python-project/FINDINGS.md +7 -0
  183. package/starter/templates/python-project/reference/README.md +4 -0
  184. package/starter/templates/python-project/summary.md +7 -0
  185. package/starter/templates/python-project/tasks.md +10 -0
@@ -0,0 +1,212 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as yaml from "js-yaml";
4
+ import { debugLog } from "./shared.js";
5
+ import { errorMessage } from "./utils.js";
6
+ import { buildSharedLifecycleCommands } from "./hooks.js";
7
+ import { VERSION } from "./package-metadata.js";
8
+ import { getToolCount, renderToolCatalogMarkdown } from "./tool-registry.js";
9
+ import { isSkillEnabled } from "./skill-state.js";
10
+ const REQUIRED_SKILL_FIELDS = ["name", "description"];
11
+ export function parseSkillFrontmatter(rawContent) {
12
+ // Normalize UTF-8 BOM and Windows-style CRLF line endings before matching frontmatter
13
+ const content = rawContent.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
14
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
15
+ if (!match)
16
+ return { frontmatter: null, body: content };
17
+ try {
18
+ const parsed = yaml.load(match[1]);
19
+ return { frontmatter: parsed && typeof parsed === "object" ? parsed : null, body: match[2] };
20
+ }
21
+ catch (err) {
22
+ debugLog(`parseSkillFrontmatter: malformed YAML frontmatter: ${errorMessage(err)}`);
23
+ return { frontmatter: null, body: content };
24
+ }
25
+ }
26
+ export function validateSkillFrontmatter(content, filePath) {
27
+ const { frontmatter } = parseSkillFrontmatter(content);
28
+ const prefix = filePath ? `${filePath}: ` : "";
29
+ if (!frontmatter)
30
+ return { valid: false, errors: [`${prefix}missing or invalid YAML frontmatter`] };
31
+ const errors = [];
32
+ for (const field of REQUIRED_SKILL_FIELDS) {
33
+ if (typeof frontmatter[field] !== "string" || !frontmatter[field]) {
34
+ errors.push(`${prefix}missing required field "${field}"`);
35
+ }
36
+ }
37
+ if (frontmatter.dependencies !== undefined) {
38
+ if (!Array.isArray(frontmatter.dependencies)) {
39
+ errors.push(`${prefix}"dependencies" must be an array`);
40
+ }
41
+ else if (frontmatter.dependencies.some((d) => typeof d !== "string")) {
42
+ errors.push(`${prefix}"dependencies" entries must be strings`);
43
+ }
44
+ }
45
+ if (frontmatter.hooks !== undefined && (typeof frontmatter.hooks !== "object" || frontmatter.hooks === null)) {
46
+ errors.push(`${prefix}"hooks" must be an object`);
47
+ }
48
+ if (frontmatter.version !== undefined && typeof frontmatter.version !== "string") {
49
+ errors.push(`${prefix}"version" must be a string`);
50
+ }
51
+ if (frontmatter.command !== undefined && typeof frontmatter.command !== "string") {
52
+ errors.push(`${prefix}"command" must be a string`);
53
+ }
54
+ if (frontmatter.aliases !== undefined) {
55
+ if (!Array.isArray(frontmatter.aliases)) {
56
+ errors.push(`${prefix}"aliases" must be an array`);
57
+ }
58
+ else if (frontmatter.aliases.some((alias) => typeof alias !== "string")) {
59
+ errors.push(`${prefix}"aliases" entries must be strings`);
60
+ }
61
+ }
62
+ return {
63
+ valid: errors.length === 0,
64
+ errors,
65
+ frontmatter: errors.length === 0 ? frontmatter : undefined,
66
+ };
67
+ }
68
+ export function validateSkillsDir(skillsDir) {
69
+ if (!fs.existsSync(skillsDir))
70
+ return [];
71
+ const results = [];
72
+ for (const entry of fs.readdirSync(skillsDir)) {
73
+ const entryPath = path.join(skillsDir, entry);
74
+ const stat = fs.statSync(entryPath);
75
+ if (stat.isDirectory()) {
76
+ const skillFile = path.join(entryPath, "SKILL.md");
77
+ if (fs.existsSync(skillFile)) {
78
+ results.push(validateSkillFrontmatter(fs.readFileSync(skillFile, "utf8"), skillFile));
79
+ }
80
+ }
81
+ else if (stat.isFile() && entry.endsWith(".md")) {
82
+ results.push(validateSkillFrontmatter(fs.readFileSync(entryPath, "utf8"), entryPath));
83
+ }
84
+ }
85
+ return results;
86
+ }
87
+ export function readSkillManifestHooks(phrenPath) {
88
+ const manifestPath = path.join(phrenPath, "phren.SKILL.md");
89
+ if (!fs.existsSync(manifestPath))
90
+ return null;
91
+ const content = fs.readFileSync(manifestPath, "utf8");
92
+ const { frontmatter } = parseSkillFrontmatter(content);
93
+ if (!frontmatter || typeof frontmatter.hooks !== "object" || !frontmatter.hooks)
94
+ return null;
95
+ const hooks = frontmatter.hooks;
96
+ const result = {};
97
+ for (const [event, value] of Object.entries(hooks)) {
98
+ if (!Array.isArray(value) || !value[0])
99
+ continue;
100
+ const entry = value[0];
101
+ const hooksList = entry.hooks;
102
+ if (!Array.isArray(hooksList) || !hooksList[0])
103
+ continue;
104
+ const hookDef = hooksList[0];
105
+ if (typeof hookDef.command === "string") {
106
+ result[event] = hookDef.command;
107
+ }
108
+ }
109
+ return Object.keys(result).length > 0 ? result : null;
110
+ }
111
+ // ── Skill linking helpers ───────────────────────────────────────────────────
112
+ function cleanupManagedSkillLinks(destDir, expectedNames, managedRoot) {
113
+ if (!fs.existsSync(destDir))
114
+ return;
115
+ for (const entry of fs.readdirSync(destDir)) {
116
+ if (expectedNames.has(entry))
117
+ continue;
118
+ const destPath = path.join(destDir, entry);
119
+ try {
120
+ const stat = fs.lstatSync(destPath);
121
+ if (!stat.isSymbolicLink())
122
+ continue;
123
+ const target = fs.readlinkSync(destPath);
124
+ const resolvedTarget = path.resolve(path.dirname(destPath), target);
125
+ const managedPrefix = path.resolve(managedRoot) + path.sep;
126
+ if (!resolvedTarget.startsWith(managedPrefix))
127
+ continue;
128
+ fs.unlinkSync(destPath);
129
+ }
130
+ catch (err) {
131
+ if ((process.env.PHREN_DEBUG || process.env.PHREN_DEBUG))
132
+ process.stderr.write(`[phren] cleanupManagedSkillLinks: ${errorMessage(err)}\n`);
133
+ }
134
+ }
135
+ }
136
+ export function linkSkillsDir(srcDir, destDir, managedRoot, symlinkFile, opts) {
137
+ if (!fs.existsSync(srcDir))
138
+ return;
139
+ fs.mkdirSync(destDir, { recursive: true });
140
+ const expectedNames = new Set();
141
+ for (const entry of fs.readdirSync(srcDir)) {
142
+ const srcPath = path.join(srcDir, entry);
143
+ const stat = fs.statSync(srcPath);
144
+ const skillName = stat.isDirectory() ? entry : entry.replace(/\.md$/, "");
145
+ if (opts?.phrenPath && opts.scope && !isSkillEnabled(opts.phrenPath, opts.scope, skillName)) {
146
+ continue;
147
+ }
148
+ if (stat.isFile() && entry.endsWith(".md")) {
149
+ expectedNames.add(entry);
150
+ symlinkFile(srcPath, path.join(destDir, entry), managedRoot);
151
+ }
152
+ else if (stat.isDirectory()) {
153
+ const skillFile = path.join(srcPath, "SKILL.md");
154
+ if (fs.existsSync(skillFile)) {
155
+ expectedNames.add(entry);
156
+ // Symlink the entire skill directory so bundled scripts and assets are accessible.
157
+ // Relative paths in the skill body remain valid because the directory structure is preserved.
158
+ symlinkFile(srcPath, path.join(destDir, entry), managedRoot);
159
+ }
160
+ }
161
+ }
162
+ cleanupManagedSkillLinks(destDir, expectedNames, managedRoot);
163
+ }
164
+ export function writeSkillMd(phrenPath) {
165
+ const lifecycle = buildSharedLifecycleCommands();
166
+ const sessionStartCmd = lifecycle.sessionStart.replace(/"/g, '\\"');
167
+ const promptCmd = lifecycle.userPromptSubmit.replace(/"/g, '\\"');
168
+ const stopCmd = lifecycle.stop.replace(/"/g, '\\"');
169
+ const version = VERSION;
170
+ const toolCount = getToolCount();
171
+ const toolCatalog = renderToolCatalogMarkdown();
172
+ const content = `---
173
+ name: phren
174
+ description: Long-term memory for your AI agents with automatic context injection and finding capture
175
+ version: "${version}"
176
+ license: MIT
177
+ hooks:
178
+ SessionStart:
179
+ - hooks:
180
+ - type: command
181
+ command: "${sessionStartCmd}"
182
+ UserPromptSubmit:
183
+ - hooks:
184
+ - type: command
185
+ command: "${promptCmd}"
186
+ timeout: 3
187
+ Stop:
188
+ - hooks:
189
+ - type: command
190
+ command: "${stopCmd}"
191
+ ---
192
+
193
+ # phren
194
+
195
+ Long-term memory for your AI agents. Injects relevant project context at the start of
196
+ each prompt and saves findings at session end via git. Works with Claude Code, Copilot CLI,
197
+ Cursor, Codex, and more.
198
+
199
+ ## Lifecycle hooks
200
+
201
+ - **SessionStart**: pulls latest phren data and self-heals hook/symlink drift
202
+ - **UserPromptSubmit**: searches phren, injects matching context with trust filtering and token budgeting
203
+ - **Stop**: commits and pushes any phren changes to remote
204
+
205
+ ## MCP tools (${toolCount})
206
+
207
+ ${toolCatalog}
208
+
209
+ `;
210
+ const dest = path.join(phrenPath, "phren.SKILL.md");
211
+ fs.writeFileSync(dest, content);
212
+ }