@apholdings/jensen-code 0.0.3 → 0.0.5

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 (166) hide show
  1. package/dist/cli/args.d.ts.map +1 -1
  2. package/dist/cli/args.js +6 -6
  3. package/dist/cli/args.js.map +1 -1
  4. package/dist/config.d.ts +6 -5
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +32 -25
  7. package/dist/config.js.map +1 -1
  8. package/dist/core/agent-session.d.ts +1 -0
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +25 -0
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/extensions/loader.d.ts.map +1 -1
  13. package/dist/core/extensions/loader.js +1 -1
  14. package/dist/core/extensions/loader.js.map +1 -1
  15. package/dist/core/footer-data-provider.d.ts +4 -1
  16. package/dist/core/footer-data-provider.d.ts.map +1 -1
  17. package/dist/core/footer-data-provider.js +25 -11
  18. package/dist/core/footer-data-provider.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  24. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  25. package/dist/modes/interactive/components/custom-editor.js +5 -0
  26. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  27. package/dist/modes/interactive/components/footer.d.ts +0 -2
  28. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  29. package/dist/modes/interactive/components/footer.js +8 -146
  30. package/dist/modes/interactive/components/footer.js.map +1 -1
  31. package/dist/modes/interactive/components/header.d.ts +9 -3
  32. package/dist/modes/interactive/components/header.d.ts.map +1 -1
  33. package/dist/modes/interactive/components/header.js +125 -196
  34. package/dist/modes/interactive/components/header.js.map +1 -1
  35. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  36. package/dist/modes/interactive/components/tool-execution.js +1 -2
  37. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  38. package/dist/modes/interactive/interactive-mode.d.ts +23 -4
  39. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  40. package/dist/modes/interactive/interactive-mode.js +657 -243
  41. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  42. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  43. package/dist/modes/interactive/theme/theme.js +2 -0
  44. package/dist/modes/interactive/theme/theme.js.map +1 -1
  45. package/dist/utils/frontmatter.d.ts.map +1 -1
  46. package/dist/utils/frontmatter.js +8 -4
  47. package/dist/utils/frontmatter.js.map +1 -1
  48. package/dist/utils/tools-manager.d.ts.map +1 -1
  49. package/dist/utils/tools-manager.js +2 -2
  50. package/dist/utils/tools-manager.js.map +1 -1
  51. package/examples/extensions/osgrep.ts +643 -0
  52. package/examples/extensions/subagent/agents.ts +150 -38
  53. package/examples/extensions/subagent/index.ts +634 -514
  54. package/package.json +4 -3
  55. package/examples/README.md +0 -25
  56. package/examples/extensions/README.md +0 -206
  57. package/examples/extensions/antigravity-image-gen.ts +0 -416
  58. package/examples/extensions/auto-commit-on-exit.ts +0 -50
  59. package/examples/extensions/bash-spawn-hook.ts +0 -31
  60. package/examples/extensions/bookmark.ts +0 -51
  61. package/examples/extensions/built-in-tool-renderer.ts +0 -247
  62. package/examples/extensions/claude-rules.ts +0 -87
  63. package/examples/extensions/commands.ts +0 -73
  64. package/examples/extensions/confirm-destructive.ts +0 -60
  65. package/examples/extensions/custom-compaction.ts +0 -115
  66. package/examples/extensions/custom-footer.ts +0 -65
  67. package/examples/extensions/custom-header.ts +0 -74
  68. package/examples/extensions/custom-provider-anthropic/index.ts +0 -605
  69. package/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  70. package/examples/extensions/custom-provider-anthropic/package.json +0 -19
  71. package/examples/extensions/custom-provider-gitlab-duo/index.ts +0 -350
  72. package/examples/extensions/custom-provider-gitlab-duo/package.json +0 -16
  73. package/examples/extensions/custom-provider-gitlab-duo/test.ts +0 -82
  74. package/examples/extensions/custom-provider-qwen-cli/index.ts +0 -346
  75. package/examples/extensions/custom-provider-qwen-cli/package.json +0 -16
  76. package/examples/extensions/dirty-repo-guard.ts +0 -57
  77. package/examples/extensions/doom-overlay/README.md +0 -46
  78. package/examples/extensions/doom-overlay/doom/build/doom.js +0 -21
  79. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  80. package/examples/extensions/doom-overlay/doom/build.sh +0 -152
  81. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +0 -72
  82. package/examples/extensions/doom-overlay/doom-component.ts +0 -132
  83. package/examples/extensions/doom-overlay/doom-engine.ts +0 -173
  84. package/examples/extensions/doom-overlay/doom-keys.ts +0 -104
  85. package/examples/extensions/doom-overlay/index.ts +0 -75
  86. package/examples/extensions/doom-overlay/wad-finder.ts +0 -51
  87. package/examples/extensions/dynamic-resources/SKILL.md +0 -8
  88. package/examples/extensions/dynamic-resources/dynamic.json +0 -79
  89. package/examples/extensions/dynamic-resources/dynamic.md +0 -5
  90. package/examples/extensions/dynamic-resources/index.ts +0 -16
  91. package/examples/extensions/dynamic-tools.ts +0 -75
  92. package/examples/extensions/event-bus.ts +0 -44
  93. package/examples/extensions/file-trigger.ts +0 -42
  94. package/examples/extensions/git-checkpoint.ts +0 -54
  95. package/examples/extensions/handoff.ts +0 -151
  96. package/examples/extensions/hello.ts +0 -26
  97. package/examples/extensions/inline-bash.ts +0 -95
  98. package/examples/extensions/input-transform.ts +0 -44
  99. package/examples/extensions/interactive-shell.ts +0 -197
  100. package/examples/extensions/mac-system-theme.ts +0 -48
  101. package/examples/extensions/message-renderer.ts +0 -60
  102. package/examples/extensions/minimal-mode.ts +0 -427
  103. package/examples/extensions/modal-editor.ts +0 -86
  104. package/examples/extensions/model-status.ts +0 -32
  105. package/examples/extensions/notify.ts +0 -56
  106. package/examples/extensions/overlay-qa-tests.ts +0 -1349
  107. package/examples/extensions/overlay-test.ts +0 -151
  108. package/examples/extensions/permission-gate.ts +0 -35
  109. package/examples/extensions/pirate.ts +0 -48
  110. package/examples/extensions/plan-mode/README.md +0 -65
  111. package/examples/extensions/plan-mode/index.ts +0 -341
  112. package/examples/extensions/plan-mode/utils.ts +0 -168
  113. package/examples/extensions/preset.ts +0 -399
  114. package/examples/extensions/protected-paths.ts +0 -31
  115. package/examples/extensions/provider-payload.ts +0 -15
  116. package/examples/extensions/qna.ts +0 -120
  117. package/examples/extensions/question.ts +0 -265
  118. package/examples/extensions/questionnaire.ts +0 -428
  119. package/examples/extensions/rainbow-editor.ts +0 -89
  120. package/examples/extensions/reload-runtime.ts +0 -38
  121. package/examples/extensions/rpc-demo.ts +0 -125
  122. package/examples/extensions/sandbox/index.ts +0 -319
  123. package/examples/extensions/sandbox/package-lock.json +0 -92
  124. package/examples/extensions/sandbox/package.json +0 -19
  125. package/examples/extensions/send-user-message.ts +0 -98
  126. package/examples/extensions/session-name.ts +0 -28
  127. package/examples/extensions/shutdown-command.ts +0 -64
  128. package/examples/extensions/snake.ts +0 -344
  129. package/examples/extensions/space-invaders.ts +0 -561
  130. package/examples/extensions/ssh.ts +0 -221
  131. package/examples/extensions/status-line.ts +0 -41
  132. package/examples/extensions/subagent/README.md +0 -172
  133. package/examples/extensions/subagent/agents/planner.md +0 -37
  134. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  135. package/examples/extensions/subagent/agents/scout.md +0 -50
  136. package/examples/extensions/subagent/agents/worker.md +0 -24
  137. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  138. package/examples/extensions/subagent/prompts/implement.md +0 -10
  139. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
  140. package/examples/extensions/summarize.ts +0 -196
  141. package/examples/extensions/system-prompt-header.ts +0 -18
  142. package/examples/extensions/timed-confirm.ts +0 -71
  143. package/examples/extensions/titlebar-spinner.ts +0 -59
  144. package/examples/extensions/todo.ts +0 -300
  145. package/examples/extensions/tool-override.ts +0 -144
  146. package/examples/extensions/tools.ts +0 -147
  147. package/examples/extensions/trigger-compact.ts +0 -41
  148. package/examples/extensions/truncated-tool.ts +0 -193
  149. package/examples/extensions/widget-placement.ts +0 -18
  150. package/examples/extensions/with-deps/index.ts +0 -33
  151. package/examples/extensions/with-deps/package-lock.json +0 -31
  152. package/examples/extensions/with-deps/package.json +0 -22
  153. package/examples/rpc-extension-ui.ts +0 -632
  154. package/examples/sdk/01-minimal.ts +0 -23
  155. package/examples/sdk/02-custom-model.ts +0 -50
  156. package/examples/sdk/03-custom-prompt.ts +0 -56
  157. package/examples/sdk/04-skills.ts +0 -47
  158. package/examples/sdk/05-tools.ts +0 -57
  159. package/examples/sdk/06-extensions.ts +0 -89
  160. package/examples/sdk/07-context-files.ts +0 -41
  161. package/examples/sdk/08-prompt-templates.ts +0 -48
  162. package/examples/sdk/09-api-keys-and-oauth.ts +0 -49
  163. package/examples/sdk/10-settings.ts +0 -52
  164. package/examples/sdk/11-sessions.ts +0 -49
  165. package/examples/sdk/12-full-control.ts +0 -83
  166. 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,8 +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
  }
127
-
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
+ }