@apholdings/jensen-code 0.0.4 → 0.0.6

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 (161) hide show
  1. package/CHANGELOG.md +3061 -3061
  2. package/README.md +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +6 -6
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/config.d.ts +17 -6
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +55 -28
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +10 -0
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/modes/interactive/components/assistant-message.d.ts +1 -6
  18. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  19. package/dist/modes/interactive/components/assistant-message.js +10 -40
  20. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  21. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  22. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  23. package/dist/modes/interactive/components/custom-editor.js +5 -0
  24. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  25. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/tool-execution.js +1 -2
  27. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  28. package/dist/modes/interactive/components/top-bar.d.ts.map +1 -1
  29. package/dist/modes/interactive/components/top-bar.js +1 -1
  30. package/dist/modes/interactive/components/top-bar.js.map +1 -1
  31. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  32. package/dist/modes/interactive/components/user-message.js +1 -1
  33. package/dist/modes/interactive/components/user-message.js.map +1 -1
  34. package/dist/modes/interactive/interactive-mode.d.ts +6 -3
  35. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  36. package/dist/modes/interactive/interactive-mode.js +204 -86
  37. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  38. package/dist/utils/frontmatter.d.ts.map +1 -1
  39. package/dist/utils/frontmatter.js +8 -4
  40. package/dist/utils/frontmatter.js.map +1 -1
  41. package/dist/utils/tools-manager.d.ts.map +1 -1
  42. package/dist/utils/tools-manager.js +2 -2
  43. package/dist/utils/tools-manager.js.map +1 -1
  44. package/docs/custom-provider.md +592 -592
  45. package/docs/session.md +412 -412
  46. package/examples/extensions/osgrep.ts +643 -0
  47. package/examples/extensions/subagent/agents.ts +150 -37
  48. package/examples/extensions/subagent/index.ts +634 -513
  49. package/package.json +3 -3
  50. package/examples/README.md +0 -25
  51. package/examples/extensions/README.md +0 -206
  52. package/examples/extensions/antigravity-image-gen.ts +0 -415
  53. package/examples/extensions/auto-commit-on-exit.ts +0 -49
  54. package/examples/extensions/bash-spawn-hook.ts +0 -30
  55. package/examples/extensions/bookmark.ts +0 -50
  56. package/examples/extensions/built-in-tool-renderer.ts +0 -246
  57. package/examples/extensions/claude-rules.ts +0 -86
  58. package/examples/extensions/commands.ts +0 -72
  59. package/examples/extensions/confirm-destructive.ts +0 -59
  60. package/examples/extensions/custom-compaction.ts +0 -114
  61. package/examples/extensions/custom-footer.ts +0 -64
  62. package/examples/extensions/custom-header.ts +0 -73
  63. package/examples/extensions/custom-provider-anthropic/index.ts +0 -604
  64. package/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  65. package/examples/extensions/custom-provider-anthropic/package.json +0 -19
  66. package/examples/extensions/custom-provider-gitlab-duo/index.ts +0 -349
  67. package/examples/extensions/custom-provider-gitlab-duo/package.json +0 -16
  68. package/examples/extensions/custom-provider-gitlab-duo/test.ts +0 -82
  69. package/examples/extensions/custom-provider-qwen-cli/index.ts +0 -345
  70. package/examples/extensions/custom-provider-qwen-cli/package.json +0 -16
  71. package/examples/extensions/dirty-repo-guard.ts +0 -56
  72. package/examples/extensions/doom-overlay/README.md +0 -46
  73. package/examples/extensions/doom-overlay/doom/build/doom.js +0 -21
  74. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  75. package/examples/extensions/doom-overlay/doom/build.sh +0 -152
  76. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +0 -72
  77. package/examples/extensions/doom-overlay/doom-component.ts +0 -132
  78. package/examples/extensions/doom-overlay/doom-engine.ts +0 -173
  79. package/examples/extensions/doom-overlay/doom-keys.ts +0 -104
  80. package/examples/extensions/doom-overlay/index.ts +0 -74
  81. package/examples/extensions/doom-overlay/wad-finder.ts +0 -51
  82. package/examples/extensions/dynamic-resources/SKILL.md +0 -8
  83. package/examples/extensions/dynamic-resources/dynamic.json +0 -79
  84. package/examples/extensions/dynamic-resources/dynamic.md +0 -5
  85. package/examples/extensions/dynamic-resources/index.ts +0 -15
  86. package/examples/extensions/dynamic-tools.ts +0 -74
  87. package/examples/extensions/event-bus.ts +0 -43
  88. package/examples/extensions/file-trigger.ts +0 -41
  89. package/examples/extensions/git-checkpoint.ts +0 -53
  90. package/examples/extensions/handoff.ts +0 -150
  91. package/examples/extensions/hello.ts +0 -25
  92. package/examples/extensions/inline-bash.ts +0 -94
  93. package/examples/extensions/input-transform.ts +0 -43
  94. package/examples/extensions/interactive-shell.ts +0 -196
  95. package/examples/extensions/mac-system-theme.ts +0 -47
  96. package/examples/extensions/message-renderer.ts +0 -59
  97. package/examples/extensions/minimal-mode.ts +0 -426
  98. package/examples/extensions/modal-editor.ts +0 -85
  99. package/examples/extensions/model-status.ts +0 -31
  100. package/examples/extensions/notify.ts +0 -55
  101. package/examples/extensions/overlay-qa-tests.ts +0 -1348
  102. package/examples/extensions/overlay-test.ts +0 -150
  103. package/examples/extensions/permission-gate.ts +0 -34
  104. package/examples/extensions/pirate.ts +0 -47
  105. package/examples/extensions/plan-mode/README.md +0 -65
  106. package/examples/extensions/plan-mode/index.ts +0 -340
  107. package/examples/extensions/plan-mode/utils.ts +0 -168
  108. package/examples/extensions/preset.ts +0 -398
  109. package/examples/extensions/protected-paths.ts +0 -30
  110. package/examples/extensions/provider-payload.ts +0 -14
  111. package/examples/extensions/qna.ts +0 -119
  112. package/examples/extensions/question.ts +0 -264
  113. package/examples/extensions/questionnaire.ts +0 -427
  114. package/examples/extensions/rainbow-editor.ts +0 -88
  115. package/examples/extensions/reload-runtime.ts +0 -37
  116. package/examples/extensions/rpc-demo.ts +0 -124
  117. package/examples/extensions/sandbox/index.ts +0 -318
  118. package/examples/extensions/sandbox/package-lock.json +0 -92
  119. package/examples/extensions/sandbox/package.json +0 -19
  120. package/examples/extensions/send-user-message.ts +0 -97
  121. package/examples/extensions/session-name.ts +0 -27
  122. package/examples/extensions/shutdown-command.ts +0 -63
  123. package/examples/extensions/snake.ts +0 -343
  124. package/examples/extensions/space-invaders.ts +0 -560
  125. package/examples/extensions/ssh.ts +0 -220
  126. package/examples/extensions/status-line.ts +0 -40
  127. package/examples/extensions/subagent/README.md +0 -172
  128. package/examples/extensions/subagent/agents/planner.md +0 -37
  129. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  130. package/examples/extensions/subagent/agents/scout.md +0 -50
  131. package/examples/extensions/subagent/agents/worker.md +0 -24
  132. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  133. package/examples/extensions/subagent/prompts/implement.md +0 -10
  134. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
  135. package/examples/extensions/summarize.ts +0 -195
  136. package/examples/extensions/system-prompt-header.ts +0 -17
  137. package/examples/extensions/timed-confirm.ts +0 -70
  138. package/examples/extensions/titlebar-spinner.ts +0 -58
  139. package/examples/extensions/todo.ts +0 -299
  140. package/examples/extensions/tool-override.ts +0 -143
  141. package/examples/extensions/tools.ts +0 -146
  142. package/examples/extensions/trigger-compact.ts +0 -40
  143. package/examples/extensions/truncated-tool.ts +0 -192
  144. package/examples/extensions/widget-placement.ts +0 -17
  145. package/examples/extensions/with-deps/index.ts +0 -32
  146. package/examples/extensions/with-deps/package-lock.json +0 -31
  147. package/examples/extensions/with-deps/package.json +0 -22
  148. package/examples/rpc-extension-ui.ts +0 -632
  149. package/examples/sdk/01-minimal.ts +0 -22
  150. package/examples/sdk/02-custom-model.ts +0 -49
  151. package/examples/sdk/03-custom-prompt.ts +0 -55
  152. package/examples/sdk/04-skills.ts +0 -46
  153. package/examples/sdk/05-tools.ts +0 -56
  154. package/examples/sdk/06-extensions.ts +0 -88
  155. package/examples/sdk/07-context-files.ts +0 -40
  156. package/examples/sdk/08-prompt-templates.ts +0 -47
  157. package/examples/sdk/09-api-keys-and-oauth.ts +0 -48
  158. package/examples/sdk/10-settings.ts +0 -51
  159. package/examples/sdk/11-sessions.ts +0 -48
  160. package/examples/sdk/12-full-control.ts +0 -82
  161. package/examples/sdk/README.md +0 -145
@@ -4,9 +4,10 @@
4
4
 
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
- import { getAgentDir, parseFrontmatter } from "@apholdings/jensen-code";
7
+ import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@apholdings/jensen-code";
8
8
 
9
9
  export type AgentScope = "user" | "project" | "both";
10
+ export type AgentSource = "user" | "project";
10
11
 
11
12
  export interface AgentConfig {
12
13
  name: string;
@@ -14,105 +15,209 @@ export interface AgentConfig {
14
15
  tools?: string[];
15
16
  model?: string;
16
17
  systemPrompt: string;
17
- source: "user" | "project";
18
+ source: AgentSource;
18
19
  filePath: string;
19
20
  }
20
21
 
22
+ export type AgentDiscoveryErrorCode = "read_error" | "parse_error" | "validation_error";
23
+
24
+ export interface AgentDiscoveryError {
25
+ code: AgentDiscoveryErrorCode;
26
+ path: string;
27
+ source: AgentSource;
28
+ reason: string;
29
+ }
30
+
21
31
  export interface AgentDiscoveryResult {
22
32
  agents: AgentConfig[];
23
33
  projectAgentsDir: string | null;
34
+ errors: AgentDiscoveryError[];
35
+ }
36
+
37
+ function normalizeFsPath(filePath: string): string {
38
+ return path.normalize(path.resolve(filePath));
39
+ }
40
+
41
+ function normalizeAgentName(value: unknown): string | null {
42
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
43
+ }
44
+
45
+ function normalizeAgentDescription(value: unknown): string | null {
46
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
24
47
  }
25
48
 
26
- function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
49
+ function normalizeAgentTools(value: unknown): string[] | undefined {
50
+ if (typeof value === "string") {
51
+ const tools = value
52
+ .split(",")
53
+ .map((tool) => tool.trim())
54
+ .filter((tool) => tool.length > 0);
55
+ return tools.length > 0 ? tools : undefined;
56
+ }
57
+
58
+ if (Array.isArray(value)) {
59
+ const tools = value
60
+ .filter((tool): tool is string => typeof tool === "string")
61
+ .map((tool) => tool.trim())
62
+ .filter((tool) => tool.length > 0);
63
+ return tools.length > 0 ? tools : undefined;
64
+ }
65
+
66
+ return undefined;
67
+ }
68
+
69
+ function normalizeAgentModel(value: unknown): string | undefined {
70
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
71
+ }
72
+
73
+ function formatYamlError(error: unknown): string {
74
+ if (error instanceof Error && error.message.trim().length > 0) {
75
+ return error.message.trim();
76
+ }
77
+ return String(error);
78
+ }
79
+
80
+ function loadAgentsFromDir(dir: string, source: AgentSource): { agents: AgentConfig[]; errors: AgentDiscoveryError[] } {
27
81
  const agents: AgentConfig[] = [];
82
+ const errors: AgentDiscoveryError[] = [];
83
+ const normalizedDir = normalizeFsPath(dir);
28
84
 
29
- if (!fs.existsSync(dir)) {
30
- return agents;
85
+ if (!fs.existsSync(normalizedDir)) {
86
+ return { agents, errors };
31
87
  }
32
88
 
33
89
  let entries: fs.Dirent[];
34
90
  try {
35
- entries = fs.readdirSync(dir, { withFileTypes: true });
36
- } catch {
37
- return agents;
91
+ entries = fs.readdirSync(normalizedDir, { withFileTypes: true });
92
+ } catch (error) {
93
+ errors.push({
94
+ code: "read_error",
95
+ path: normalizedDir,
96
+ source,
97
+ reason: error instanceof Error ? error.message : String(error),
98
+ });
99
+ return { agents, errors };
38
100
  }
39
101
 
40
102
  for (const entry of entries) {
41
103
  if (!entry.name.endsWith(".md")) continue;
42
104
  if (!entry.isFile() && !entry.isSymbolicLink()) continue;
43
105
 
44
- const filePath = path.join(dir, entry.name);
106
+ const filePath = normalizeFsPath(path.join(normalizedDir, entry.name));
45
107
  let content: string;
46
108
  try {
47
109
  content = fs.readFileSync(filePath, "utf-8");
48
- } catch {
110
+ } catch (error) {
111
+ errors.push({
112
+ code: "read_error",
113
+ path: filePath,
114
+ source,
115
+ reason: error instanceof Error ? error.message : String(error),
116
+ });
49
117
  continue;
50
118
  }
51
119
 
52
- const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
53
-
54
- if (!frontmatter.name || !frontmatter.description) {
120
+ let frontmatter: Record<string, unknown>;
121
+ let body: string;
122
+ try {
123
+ const parsed = parseFrontmatter<Record<string, unknown>>(content);
124
+ frontmatter = parsed.frontmatter;
125
+ body = parsed.body;
126
+ } catch (error) {
127
+ errors.push({
128
+ code: "parse_error",
129
+ path: filePath,
130
+ source,
131
+ reason: formatYamlError(error),
132
+ });
55
133
  continue;
56
134
  }
57
135
 
58
- const tools = frontmatter.tools
59
- ?.split(",")
60
- .map((t: string) => t.trim())
61
- .filter(Boolean);
136
+ const name = normalizeAgentName(frontmatter.name);
137
+ const description = normalizeAgentDescription(frontmatter.description);
138
+ if (!name || !description) {
139
+ const missingFields = [!name ? "name" : null, !description ? "description" : null].filter(
140
+ (field): field is string => field !== null,
141
+ );
142
+ errors.push({
143
+ code: "validation_error",
144
+ path: filePath,
145
+ source,
146
+ reason: `Missing required frontmatter field${missingFields.length > 1 ? "s" : ""}: ${missingFields.join(", ")}`,
147
+ });
148
+ continue;
149
+ }
62
150
 
63
151
  agents.push({
64
- name: frontmatter.name,
65
- description: frontmatter.description,
66
- tools: tools && tools.length > 0 ? tools : undefined,
67
- model: frontmatter.model,
152
+ name,
153
+ description,
154
+ tools: normalizeAgentTools(frontmatter.tools),
155
+ model: normalizeAgentModel(frontmatter.model),
68
156
  systemPrompt: body,
69
157
  source,
70
158
  filePath,
71
159
  });
72
160
  }
73
161
 
74
- return agents;
162
+ agents.sort((left, right) => left.name.localeCompare(right.name));
163
+ errors.sort((left, right) => left.path.localeCompare(right.path));
164
+
165
+ return { agents, errors };
75
166
  }
76
167
 
77
- function isDirectory(p: string): boolean {
168
+ function isDirectory(candidatePath: string): boolean {
78
169
  try {
79
- return fs.statSync(p).isDirectory();
170
+ return fs.statSync(candidatePath).isDirectory();
80
171
  } catch {
81
172
  return false;
82
173
  }
83
174
  }
84
175
 
85
176
  function findNearestProjectAgentsDir(cwd: string): string | null {
86
- let currentDir = cwd;
177
+ let currentDir = normalizeFsPath(cwd);
178
+
87
179
  while (true) {
88
- const candidate = path.join(currentDir, ".pi", "agents");
89
- if (isDirectory(candidate)) return candidate;
180
+ const candidate = normalizeFsPath(path.join(currentDir, CONFIG_DIR_NAME, "agents"));
181
+ if (isDirectory(candidate)) {
182
+ return candidate;
183
+ }
90
184
 
91
185
  const parentDir = path.dirname(currentDir);
92
- if (parentDir === currentDir) return null;
186
+ if (parentDir === currentDir) {
187
+ return null;
188
+ }
93
189
  currentDir = parentDir;
94
190
  }
95
191
  }
96
192
 
97
193
  export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
98
- const userDir = path.join(getAgentDir(), "agents");
194
+ const userDir = normalizeFsPath(path.join(getAgentDir(), "agents"));
99
195
  const projectAgentsDir = findNearestProjectAgentsDir(cwd);
100
196
 
101
- const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
102
- const projectAgents = scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
197
+ const userDiscovery = scope === "project" ? { agents: [], errors: [] } : loadAgentsFromDir(userDir, "user");
198
+ const projectDiscovery =
199
+ scope === "user" || !projectAgentsDir
200
+ ? { agents: [], errors: [] }
201
+ : loadAgentsFromDir(projectAgentsDir, "project");
103
202
 
104
203
  const agentMap = new Map<string, AgentConfig>();
105
204
 
106
205
  if (scope === "both") {
107
- for (const agent of userAgents) agentMap.set(agent.name, agent);
108
- for (const agent of projectAgents) agentMap.set(agent.name, agent);
206
+ for (const agent of userDiscovery.agents) agentMap.set(agent.name, agent);
207
+ for (const agent of projectDiscovery.agents) agentMap.set(agent.name, agent);
109
208
  } else if (scope === "user") {
110
- for (const agent of userAgents) agentMap.set(agent.name, agent);
209
+ for (const agent of userDiscovery.agents) agentMap.set(agent.name, agent);
111
210
  } else {
112
- for (const agent of projectAgents) agentMap.set(agent.name, agent);
211
+ for (const agent of projectDiscovery.agents) agentMap.set(agent.name, agent);
113
212
  }
114
213
 
115
- return { agents: Array.from(agentMap.values()), projectAgentsDir };
214
+ return {
215
+ agents: Array.from(agentMap.values()).sort((left, right) => left.name.localeCompare(right.name)),
216
+ projectAgentsDir,
217
+ errors: [...userDiscovery.errors, ...projectDiscovery.errors].sort((left, right) =>
218
+ left.path.localeCompare(right.path),
219
+ ),
220
+ };
116
221
  }
117
222
 
118
223
  export function formatAgentList(agents: AgentConfig[], maxItems: number): { text: string; remaining: number } {
@@ -120,7 +225,15 @@ export function formatAgentList(agents: AgentConfig[], maxItems: number): { text
120
225
  const listed = agents.slice(0, maxItems);
121
226
  const remaining = agents.length - listed.length;
122
227
  return {
123
- text: listed.map((a) => `${a.name} (${a.source}): ${a.description}`).join("; "),
228
+ text: listed.map((agent) => `${agent.name} (${agent.source}): ${agent.description}`).join("; "),
124
229
  remaining,
125
230
  };
126
231
  }
232
+
233
+ export function findDiscoveryErrorForAgent(
234
+ errors: AgentDiscoveryError[],
235
+ agentName: string,
236
+ ): AgentDiscoveryError | undefined {
237
+ const expectedFileName = `${agentName}.md`.toLowerCase();
238
+ return errors.find((error) => path.basename(error.path).toLowerCase() === expectedFileName);
239
+ }