@oh-my-pi/pi-coding-agent 16.2.3 → 16.2.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 (49) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/cli.js +3449 -3438
  3. package/dist/types/cli/update-cli.d.ts +15 -0
  4. package/dist/types/collab/replication-shrink.d.ts +39 -0
  5. package/dist/types/config/provider-globals.d.ts +7 -0
  6. package/dist/types/memories/index.d.ts +20 -1
  7. package/dist/types/modes/components/status-line/component.d.ts +5 -0
  8. package/dist/types/modes/components/status-line/types.d.ts +10 -0
  9. package/dist/types/modes/controllers/command-controller.d.ts +3 -3
  10. package/dist/types/modes/interactive-mode.d.ts +1 -0
  11. package/dist/types/modes/theme/theme.d.ts +2 -1
  12. package/dist/types/modes/types.d.ts +2 -1
  13. package/dist/types/session/agent-session.d.ts +7 -0
  14. package/dist/types/session/session-manager.d.ts +2 -3
  15. package/dist/types/task/provider-concurrency.d.ts +40 -0
  16. package/dist/types/utils/git.d.ts +11 -0
  17. package/package.json +12 -12
  18. package/src/autolearn/controller.ts +13 -22
  19. package/src/cli/update-cli.ts +254 -0
  20. package/src/cli/worktree-cli.ts +8 -5
  21. package/src/collab/host.ts +13 -8
  22. package/src/collab/replication-shrink.ts +111 -0
  23. package/src/config/model-discovery.ts +33 -13
  24. package/src/config/provider-globals.ts +25 -0
  25. package/src/config/settings-schema.ts +5 -2
  26. package/src/eval/__tests__/julia-prelude.test.ts +2 -2
  27. package/src/memories/index.ts +115 -9
  28. package/src/memory-backend/local-backend.ts +5 -3
  29. package/src/modes/components/status-line/component.ts +35 -2
  30. package/src/modes/components/status-line/segments.ts +22 -8
  31. package/src/modes/components/status-line/types.ts +7 -0
  32. package/src/modes/controllers/command-controller.ts +5 -35
  33. package/src/modes/controllers/event-controller.ts +7 -1
  34. package/src/modes/controllers/input-controller.ts +1 -1
  35. package/src/modes/interactive-mode.ts +9 -20
  36. package/src/modes/theme/theme.ts +6 -0
  37. package/src/modes/types.ts +2 -1
  38. package/src/prompts/goals/goal-todo-context.md +12 -0
  39. package/src/sdk.ts +13 -5
  40. package/src/session/agent-session.ts +129 -14
  41. package/src/session/session-manager.ts +2 -3
  42. package/src/slash-commands/builtin-registry.ts +7 -21
  43. package/src/task/executor.ts +4 -62
  44. package/src/task/provider-concurrency.ts +100 -0
  45. package/src/task/worktree.ts +13 -4
  46. package/src/task/yield-assembly.ts +27 -39
  47. package/src/tools/read.ts +11 -1
  48. package/src/utils/git.ts +13 -0
  49. package/src/prompts/system/autolearn-nudge.md +0 -5
@@ -130,74 +130,62 @@ export function assembleYieldResult(
130
130
  arrayLabels?: ReadonlySet<string>,
131
131
  ): AssembledYieldResult | undefined {
132
132
  if (yieldItems.length === 0) return undefined;
133
+
134
+ // Terminal = the last non-incremental yield (untyped, or string-typed like
135
+ // `type: "result"`). Array-typed yields are incremental sections and never
136
+ // terminate on their own.
133
137
  let terminalItem: YieldItem | undefined;
134
138
  for (let index = yieldItems.length - 1; index >= 0; index--) {
135
139
  const item = yieldItems[index];
136
- if (!item) continue;
137
- if (!isIncrementalYieldType(item.type)) {
140
+ if (item && !isIncrementalYieldType(item.type)) {
138
141
  terminalItem = item;
139
142
  break;
140
143
  }
141
144
  }
142
- let hasTypedSections = false;
143
- for (const item of yieldItems) {
144
- if (getYieldLabels(item.type).length > 0) {
145
- hasTypedSections = true;
146
- break;
147
- }
148
- }
149
- if (terminalItem && typeof terminalItem.type === "string" && terminalItem.data === undefined) {
150
- const resolved = resolveYieldPayload(terminalItem, lastAssistantText, getYieldLabels(terminalItem.type));
151
- return {
152
- data: resolved.value,
153
- schemaOverridden: terminalItem.schemaOverridden === true,
154
- rawText: resolved.fromLastAssistantText && typeof resolved.value === "string",
155
- missingData: resolved.missingData,
156
- };
157
- }
158
- if (!hasTypedSections && terminalItem) {
159
- const resolved = resolveYieldPayload(terminalItem, lastAssistantText, []);
160
- return {
161
- data: resolved.value,
162
- schemaOverridden: terminalItem.schemaOverridden === true,
163
- rawText: resolved.fromLastAssistantText && typeof resolved.value === "string",
164
- missingData: resolved.missingData,
165
- };
166
- }
167
145
 
146
+ // Sections come ONLY from incremental (array-typed) yields. A string `type`
147
+ // is a terminal marker, never a section label: folding its data under the
148
+ // label is what nested a finalize payload (`type: "result"`, `data: {…}`) one
149
+ // level deep and made output-schema validation report every field missing.
168
150
  const sections: Record<string, unknown> = {};
169
151
  const sectionCounts = new Map<string, number>();
170
152
  let schemaOverridden = false;
171
153
  let missingData = false;
172
154
  let hasSections = false;
173
-
174
155
  for (const item of yieldItems) {
175
156
  if (item.status === "aborted") continue;
157
+ if (!isIncrementalYieldType(item.type)) continue;
176
158
  schemaOverridden ||= item.schemaOverridden === true;
177
159
  const labels = getYieldLabels(item.type);
178
- if (labels.length === 0) continue;
179
160
  const resolved = resolveYieldPayload(item, lastAssistantText, labels);
180
161
  missingData ||= resolved.missingData;
181
- const incremental = isIncrementalYieldType(item.type);
182
162
  for (const label of labels) {
183
- appendYieldSection(
184
- sections,
185
- sectionCounts,
186
- label,
187
- resolved.value,
188
- incremental && (arrayLabels?.has(label) ?? false),
189
- );
163
+ appendYieldSection(sections, sectionCounts, label, resolved.value, arrayLabels?.has(label) ?? false);
190
164
  hasSections = true;
191
165
  }
192
- if (!isIncrementalYieldType(item.type)) break;
193
166
  }
194
167
 
168
+ // An explicit terminal payload wins: an untyped final result or a
169
+ // `type: "result"` finalize that carries `data` is the complete result, used
170
+ // verbatim — never wrapped in a section.
171
+ if (terminalItem && terminalItem.data !== undefined) {
172
+ const resolved = resolveYieldPayload(terminalItem, lastAssistantText, []);
173
+ return {
174
+ data: resolved.value,
175
+ schemaOverridden: terminalItem.schemaOverridden === true,
176
+ rawText: resolved.fromLastAssistantText && typeof resolved.value === "string",
177
+ missingData: resolved.missingData,
178
+ };
179
+ }
180
+
181
+ // A data-less terminal finalize keeps accumulated sections; only when none
182
+ // exist does the last assistant turn become the raw result.
195
183
  if (hasSections) {
196
184
  return { data: sections, schemaOverridden, rawText: false, missingData };
197
185
  }
198
186
 
199
187
  if (!terminalItem) return undefined;
200
- const resolved = resolveYieldPayload(terminalItem, lastAssistantText, []);
188
+ const resolved = resolveYieldPayload(terminalItem, lastAssistantText, getYieldLabels(terminalItem.type));
201
189
  return {
202
190
  data: resolved.value,
203
191
  schemaOverridden: terminalItem.schemaOverridden === true,
package/src/tools/read.ts CHANGED
@@ -178,7 +178,17 @@ interface HashlineHeaderContext {
178
178
  }
179
179
 
180
180
  function formatReadHashlineHeader(displayPath: string, tag: string): string {
181
- return formatHashlineHeader(path.basename(displayPath), tag);
181
+ // In-workspace reads collapse to the bare filename for brevity: the edit
182
+ // tool's snapshot-tag recovery rebinds a bare `[name#tag]` onto the in-tree
183
+ // file it uniquely names. Out-of-workspace reads can't lean on that —
184
+ // recovery refuses to redirect a write outside the cwd/sandbox
185
+ // (HashlineFilesystem.allowTagPathRecovery) — so an absolute displayPath
186
+ // must stay directly resolvable, otherwise the basename resolves against
187
+ // cwd, misses, and the edit fails with "File not found" (e.g. ~/.claude/*).
188
+ // `shortenPath` keeps `~/.claude/...` (round-trips through resolveToCwd's ~
189
+ // expansion) instead of leaking the full home path into the read output.
190
+ const anchor = path.isAbsolute(displayPath) ? shortenPath(displayPath) : path.basename(displayPath);
191
+ return formatHashlineHeader(anchor, tag);
182
192
  }
183
193
 
184
194
  function recordFullHashlineContext(
package/src/utils/git.ts CHANGED
@@ -1712,6 +1712,19 @@ export const repo = {
1712
1712
  return primaryRootFromRepositorySync(repository);
1713
1713
  },
1714
1714
 
1715
+ /**
1716
+ * Linked-worktree metadata for `cwd`, or `null` when `cwd` is the primary
1717
+ * checkout (or outside a repository). `root` is the worktree's own checkout
1718
+ * root; `primaryRoot` is the shared main checkout that names the project.
1719
+ * Resolves purely via on-disk `.git`/`commondir` walking — no subprocess —
1720
+ * so the status line may call it on every render.
1721
+ */
1722
+ linkedWorktreeSync(cwd: string): { root: string; primaryRoot: string } | null {
1723
+ const repository = resolveRepositorySync(cwd);
1724
+ if (!repository || !isLinkedWorktree(repository)) return null;
1725
+ return { root: repository.repoRoot, primaryRoot: primaryRootFromRepositorySync(repository) };
1726
+ },
1727
+
1715
1728
  /** Full GitRepository metadata (sync). */
1716
1729
  resolveSync(cwd: string): GitRepository | null {
1717
1730
  return resolveRepositorySync(cwd);
@@ -1,5 +0,0 @@
1
- Hidden auto-learn reminder (not a user request). If your previous turn produced anything reusable, capture it now with your learning tools — a repeatable procedure becomes a managed skill (`manage_skill`), and a durable fact, convention, or user preference is worth remembering (`learn`, when memory is enabled).
2
-
3
- Only capture what will genuinely help next time. If nothing is worth keeping, do nothing.
4
-
5
- This reminder is appended to the user's real message; answer that message normally — the capture is in addition to, not a replacement for, the work the user just asked for.