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