pi-code 1.0.22 → 1.0.23
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.
package/extensions/commands.ts
CHANGED
|
@@ -52,7 +52,7 @@ import { capForContext } from './internal/output-guard.js'
|
|
|
52
52
|
import { matchesPathRules } from './internal/path-rules.js'
|
|
53
53
|
import { type InstalledPlugin, installedPlugins } from './internal/plugins.js'
|
|
54
54
|
import { isProjectApproved } from './internal/project-approval.js'
|
|
55
|
-
import {
|
|
55
|
+
import { ancestorDirs, repoRoot } from './internal/project-root.js'
|
|
56
56
|
import { claudeSettingsChain } from './internal/settings-chain.js'
|
|
57
57
|
import { createTurnOverride } from './internal/turn-override.js'
|
|
58
58
|
|
|
@@ -120,7 +120,10 @@ function isDirectory(target: string): boolean {
|
|
|
120
120
|
* the approval walk) and is included only for approved projects. */
|
|
121
121
|
export function commandDirs(cwd: string, home: string, trusted: boolean): string[] {
|
|
122
122
|
const candidates = [path.join(claudeConfigDir(home), 'commands')]
|
|
123
|
-
|
|
123
|
+
// Claude scans every .claude/commands between cwd and the repository root, the
|
|
124
|
+
// nearest winning a name clash: collectCommands lets later directories win, so
|
|
125
|
+
// the project list goes root-first with the nearest last.
|
|
126
|
+
if (trusted) candidates.push(...ancestorDirs(cwd, path.join('.claude', 'commands')).reverse())
|
|
124
127
|
const dirs: string[] = []
|
|
125
128
|
for (const dir of candidates) {
|
|
126
129
|
if (!dirs.includes(dir) && isDirectory(dir)) dirs.push(dir)
|
|
@@ -60,6 +60,24 @@ export function findNearestFile(cwd: string, relative: string): string | null {
|
|
|
60
60
|
return findNearest(cwd, relative, false)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/** Every `relative` directory between cwd and the repository root, nearest first,
|
|
64
|
+
* matching Claude's "every .claude/<kind> between the working directory and the
|
|
65
|
+
* repository root" discovery where the entry closest to cwd wins a name clash. */
|
|
66
|
+
export function ancestorDirs(cwd: string, relative: string): string[] {
|
|
67
|
+
const boundary = repoRoot(cwd) ?? cwd
|
|
68
|
+
const found: string[] = []
|
|
69
|
+
let currentDir = cwd
|
|
70
|
+
while (true) {
|
|
71
|
+
const candidate = path.join(currentDir, relative)
|
|
72
|
+
if (statOf(candidate)?.isDirectory()) found.push(candidate)
|
|
73
|
+
if (currentDir === boundary) break
|
|
74
|
+
const parentDir = path.dirname(currentDir)
|
|
75
|
+
if (parentDir === currentDir) break
|
|
76
|
+
currentDir = parentDir
|
|
77
|
+
}
|
|
78
|
+
return found
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
/** Every `relative` file between the repository root and cwd, ordered root first,
|
|
64
82
|
* matching Claude's root-down ordering for hierarchy-loaded context. */
|
|
65
83
|
export function ancestorFiles(cwd: string, relative: string): string[] {
|
|
@@ -27,7 +27,7 @@ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
|
|
|
27
27
|
import { claudeConfigDir } from './internal/config-dir.js'
|
|
28
28
|
import { installedPlugins } from './internal/plugins.js'
|
|
29
29
|
import { isProjectApproved } from './internal/project-approval.js'
|
|
30
|
-
import { findNearestDir, findNearestFile } from './internal/project-root.js'
|
|
30
|
+
import { ancestorDirs, findNearestDir, findNearestFile } from './internal/project-root.js'
|
|
31
31
|
import { claudeSettingsChain } from './internal/settings-chain.js'
|
|
32
32
|
|
|
33
33
|
export interface OutputStyle {
|
|
@@ -85,7 +85,10 @@ function isDirectory(target: string): boolean {
|
|
|
85
85
|
*/
|
|
86
86
|
export function styleDirs(cwd: string, home: string, trusted: boolean): string[] {
|
|
87
87
|
const dirs = [path.join(claudeConfigDir(home), 'output-styles')]
|
|
88
|
-
|
|
88
|
+
// Claude loads every .claude/output-styles between cwd and the repository root,
|
|
89
|
+
// using the one closest to cwd for a name clash: styleForName is first-match,
|
|
90
|
+
// so the nearest directory goes first.
|
|
91
|
+
if (trusted) dirs.push(...ancestorDirs(cwd, path.join('.claude', 'output-styles')))
|
|
89
92
|
return dirs.filter((dir) => isDirectory(dir))
|
|
90
93
|
}
|
|
91
94
|
|
package/extensions/skills.ts
CHANGED
|
@@ -29,7 +29,7 @@ import { parseCommandFile } from './internal/command-file.js'
|
|
|
29
29
|
import { claudeConfigDir } from './internal/config-dir.js'
|
|
30
30
|
import { installedPlugins } from './internal/plugins.js'
|
|
31
31
|
import { isProjectApprovedSilently } from './internal/project-approval.js'
|
|
32
|
-
import {
|
|
32
|
+
import { ancestorDirs } from './internal/project-root.js'
|
|
33
33
|
|
|
34
34
|
function isDirectory(target: string): boolean {
|
|
35
35
|
try {
|
|
@@ -53,7 +53,10 @@ export function skillDirs(cwd: string, home: string, trusted: boolean): string[]
|
|
|
53
53
|
const dirs = Array.isArray(declared) ? declared : [typeof declared === 'string' ? declared : 'skills']
|
|
54
54
|
candidates.push(...dirs.map((dir) => path.resolve(plugin.root, String(dir))))
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
// Claude loads skills from every .claude/skills between cwd and the repository
|
|
57
|
+
// root; the list goes nearest-first so findClaudeSkill's first match is the
|
|
58
|
+
// closest definition (pi's loader receives the same order).
|
|
59
|
+
if (trusted) candidates.push(...ancestorDirs(cwd, path.join('.claude', 'skills')))
|
|
57
60
|
const dirs: string[] = []
|
|
58
61
|
for (const dir of candidates) {
|
|
59
62
|
if (!dirs.includes(dir) && isDirectory(dir)) dirs.push(dir)
|
|
@@ -13,7 +13,7 @@ import { getAgentDir, parseFrontmatter, stripFrontmatter } from '@earendil-works
|
|
|
13
13
|
import { parseToolGrants } from '../internal/command-file.js'
|
|
14
14
|
import { claudeConfigDir } from '../internal/config-dir.js'
|
|
15
15
|
import { installedPlugins } from '../internal/plugins.js'
|
|
16
|
-
import { findNearestDir } from '../internal/project-root.js'
|
|
16
|
+
import { ancestorDirs, findNearestDir } from '../internal/project-root.js'
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* `tools:` may be a comma-separated string (the Claude Code format) or a YAML block
|
|
@@ -337,13 +337,16 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
337
337
|
const userDir = path.join(getAgentDir(), 'agents')
|
|
338
338
|
const claudeUserDir = path.join(claudeConfigDir(os.homedir()), 'agents')
|
|
339
339
|
const projectPiDir = findNearestDir(cwd, path.join('.pi', 'agents'))
|
|
340
|
-
|
|
340
|
+
// Claude scans every .claude/agents between cwd and the repository root, the
|
|
341
|
+
// definition closest to cwd winning a name clash; root-first load order makes
|
|
342
|
+
// the nearer directory's entry overwrite in the map below.
|
|
343
|
+
const projectClaudeDirs = ancestorDirs(cwd, path.join('.claude', 'agents')).reverse()
|
|
341
344
|
|
|
342
345
|
// Plugins load after builtins and before the user's own dirs, so a user agent
|
|
343
346
|
// wins a name clash with a plugin's, and ~/.pi/agent/agents wins over ~/.claude.
|
|
344
347
|
const userAgents = scope === 'project' ? [] : [...loadAgentsFromDir(BUILTIN_AGENTS_DIR, 'builtin'), ...pluginAgentDirs(os.homedir()).flatMap((dir) => loadAgentsFromDir(dir, 'plugin')), ...loadAgentsFromDir(claudeUserDir, 'user'), ...loadAgentsFromDir(userDir, 'user')]
|
|
345
348
|
// project .claude/agents loads first so project .pi/agents wins on name conflicts
|
|
346
|
-
const projectAgents = scope === 'user' ? [] : [...(
|
|
349
|
+
const projectAgents = scope === 'user' ? [] : [...projectClaudeDirs.flatMap((dir) => loadAgentsFromDir(dir, 'project')), ...(projectPiDir ? loadAgentsFromDir(projectPiDir, 'project') : [])]
|
|
347
350
|
|
|
348
351
|
const agentMap = buildAgentMap(userAgents, projectAgents, scope)
|
|
349
352
|
return { agents: Array.from(agentMap.values()), projectAgentsDir: projectPiDir }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Claude Code experience for the pi coding agent: reads your .claude config (rules, commands, skills, hooks, output styles, MCP servers, agents) and adds todo, checkpoints, memory, web, and subagents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|