headlesscode 1.0.2
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/ATTRIBUTION.md +53 -0
- package/CODE_OF_CONDUCT.md +130 -0
- package/CONTRIBUTING.md +107 -0
- package/LICENSE +202 -0
- package/README.md +486 -0
- package/SECURITY.md +211 -0
- package/bin/headlesscode.mjs +83 -0
- package/package.json +63 -0
- package/shared/prompts/review-mode-prompt-short.md +93 -0
- package/shared/prompts/review-mode-prompt.md +281 -0
- package/shared/rules-code/rules.md +22 -0
- package/shared/stacks/cpp/rules.md +30 -0
- package/shared/stacks/fastapi/rules.md +30 -0
- package/shared/stacks/javascript/rules.md +37 -0
- package/shared/stacks/postgresql/rules.md +31 -0
- package/shared/stacks/python/rules.md +35 -0
- package/shared/stacks/react/rules.md +11 -0
- package/shared/stacks/typescript/rules.md +10 -0
- package/src/budget/budget.ts +221 -0
- package/src/budget/concurrency.ts +126 -0
- package/src/budget/cost.ts +309 -0
- package/src/budget/index.ts +8 -0
- package/src/checkpoints/cli.ts +256 -0
- package/src/checkpoints/service.ts +227 -0
- package/src/cli.ts +1535 -0
- package/src/cloud/docker-provider.ts +334 -0
- package/src/cloud/provider.ts +300 -0
- package/src/codeintel/call-graph.ts +78 -0
- package/src/codeintel/find-references.ts +123 -0
- package/src/codeintel/go-to-definition.ts +193 -0
- package/src/codeintel/handlers.ts +190 -0
- package/src/codeintel/import-graph.ts +173 -0
- package/src/codeintel/outline.ts +180 -0
- package/src/codeintel/position.ts +77 -0
- package/src/codeintel/program.ts +350 -0
- package/src/codeintel/rename-symbol.ts +213 -0
- package/src/codeintel/tools.ts +280 -0
- package/src/codemap/build.ts +135 -0
- package/src/codemap/cli.ts +190 -0
- package/src/codemap/extract.ts +339 -0
- package/src/codemap/files.ts +236 -0
- package/src/codemap/fingerprint.ts +65 -0
- package/src/codemap/flows.ts +62 -0
- package/src/codemap/html.ts +451 -0
- package/src/codemap/lock.ts +80 -0
- package/src/codemap/types.ts +101 -0
- package/src/codesearch/airunner-embedder.ts +185 -0
- package/src/codesearch/chunk.ts +339 -0
- package/src/codesearch/cli.ts +223 -0
- package/src/codesearch/embedder.ts +332 -0
- package/src/codesearch/files.ts +280 -0
- package/src/codesearch/index.ts +469 -0
- package/src/codesearch/ollama-embedder.ts +205 -0
- package/src/codesearch/search.ts +141 -0
- package/src/codesearch/types.ts +100 -0
- package/src/config/mode-models.ts +218 -0
- package/src/dashboard/aggregate.ts +364 -0
- package/src/dashboard/chat-thread.ts +141 -0
- package/src/dashboard/checkpoints.ts +124 -0
- package/src/dashboard/cli.ts +193 -0
- package/src/dashboard/codemap.ts +44 -0
- package/src/dashboard/files.ts +121 -0
- package/src/dashboard/page.ts +2803 -0
- package/src/dashboard/self-improvement-metrics.ts +282 -0
- package/src/dashboard/server.ts +1103 -0
- package/src/dashboard/session-launch.ts +310 -0
- package/src/dashboard/timeline.ts +273 -0
- package/src/dashboard/tool-exec.ts +107 -0
- package/src/dashboard/trend-cli.ts +141 -0
- package/src/dashboard/trend.ts +413 -0
- package/src/decision-proxy/cli.ts +261 -0
- package/src/decision-proxy/proxy.ts +569 -0
- package/src/deploy/gate-cli.ts +147 -0
- package/src/deploy/gate.ts +254 -0
- package/src/engine/condense.ts +512 -0
- package/src/engine/events.ts +428 -0
- package/src/engine/handoff.ts +71 -0
- package/src/engine/lazy-tools.ts +160 -0
- package/src/engine/local-explore.ts +653 -0
- package/src/engine/logger.ts +96 -0
- package/src/engine/loop.ts +5517 -0
- package/src/engine/parser.ts +347 -0
- package/src/engine/prompt.ts +860 -0
- package/src/engine/reports.ts +47 -0
- package/src/engine/stacks.ts +448 -0
- package/src/engine/types.ts +291 -0
- package/src/engine/usage.ts +186 -0
- package/src/github/app-auth.ts +161 -0
- package/src/github/cli.ts +448 -0
- package/src/github/installations.ts +133 -0
- package/src/github/pr.ts +321 -0
- package/src/github/provision.ts +118 -0
- package/src/github/push.ts +122 -0
- package/src/index-util.ts +50 -0
- package/src/index.ts +81 -0
- package/src/init/cli.ts +248 -0
- package/src/init/gitignore.ts +74 -0
- package/src/llm/ollama.ts +308 -0
- package/src/llm/openrouter.ts +868 -0
- package/src/llm/preflight.ts +367 -0
- package/src/llm/transcript-capture.ts +84 -0
- package/src/memory/embed.ts +110 -0
- package/src/memory/index.ts +22 -0
- package/src/memory/local.ts +259 -0
- package/src/memory/summarizer.ts +283 -0
- package/src/memory/types.ts +153 -0
- package/src/memory/uwuchat.ts +157 -0
- package/src/migrate/cli.ts +115 -0
- package/src/orchestrator/analyze-cli.ts +104 -0
- package/src/orchestrator/auto-split.ts +206 -0
- package/src/orchestrator/cleanup.ts +1003 -0
- package/src/orchestrator/cli.ts +3571 -0
- package/src/orchestrator/cost-estimate.ts +564 -0
- package/src/orchestrator/cost-history-cli.ts +242 -0
- package/src/orchestrator/cost-history.ts +397 -0
- package/src/orchestrator/git-sync.ts +250 -0
- package/src/orchestrator/index.ts +153 -0
- package/src/orchestrator/log-analysis.ts +0 -0
- package/src/orchestrator/merge-check.ts +108 -0
- package/src/orchestrator/pipeline.ts +411 -0
- package/src/orchestrator/resume.ts +1940 -0
- package/src/orchestrator/reviewer.ts +503 -0
- package/src/orchestrator/split.ts +296 -0
- package/src/orchestrator/state.ts +542 -0
- package/src/orchestrator/status.ts +697 -0
- package/src/orchestrator/verification-gate.ts +134 -0
- package/src/orchestrator/watch.ts +898 -0
- package/src/permissions/commands.ts +1083 -0
- package/src/permissions/config.ts +241 -0
- package/src/permissions/index.ts +12 -0
- package/src/permissions/protected-files.ts +96 -0
- package/src/permissions/store-protection.ts +272 -0
- package/src/project-store.ts +648 -0
- package/src/projects/cli.ts +382 -0
- package/src/qa/qa.ts +487 -0
- package/src/tools/browser/handler.ts +346 -0
- package/src/tools/browser/service.ts +406 -0
- package/src/tools/browser/smoke.ts +78 -0
- package/src/tools/browser/tool.ts +99 -0
- package/src/tools/executor.ts +2575 -0
- package/src/tools/language-detect.ts +183 -0
- package/src/tools/output-summarizer.ts +369 -0
- package/src/tools/run-tests.ts +302 -0
- package/src/tools/set-indentation-tool.ts +49 -0
- package/src/tools/test-selection.ts +160 -0
- package/src/vendor/tests/smoke.ts +103 -0
- package/src/vendor/zoo-code/VENDOR-NOTES.md +213 -0
- package/src/vendor/zoo-code/shim/anthropic.ts +71 -0
- package/src/vendor/zoo-code/shim/openai.d.ts +60 -0
- package/src/vendor/zoo-code/shim/os-name.ts +18 -0
- package/src/vendor/zoo-code/shim/strip-bom.ts +14 -0
- package/src/vendor/zoo-code/shim/vscode.ts +76 -0
- package/src/vendor/zoo-code/src/core/config/CustomModesManager.ts +1015 -0
- package/src/vendor/zoo-code/src/core/diff/strategies/multi-search-replace.ts +670 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/capabilities.ts +46 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/custom-instructions.ts +559 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/index.ts +10 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/markdown-formatting.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/modes.ts +35 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/objective.ts +13 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/rules.ts +95 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/skills.ts +105 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/system-info.ts +30 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use-guidelines.ts +9 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/system.ts +176 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/access_mcp_resource.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_diff.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_patch.ts +61 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/ask_followup_question.ts +62 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/attempt_completion.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/codebase_search.ts +43 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/converters.ts +109 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit.ts +48 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit_file.ts +72 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/execute_command.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/generate_image.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/index.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/list_files.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/mcp_server.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/new_task.ts +39 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_command_output.ts +81 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_file.ts +169 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/run_slash_command.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_files.ts +50 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_replace.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/skill.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/switch_mode.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/update_todo_list.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/write_to_file.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/types.ts +12 -0
- package/src/vendor/zoo-code/src/i18n/index.ts +19 -0
- package/src/vendor/zoo-code/src/integrations/misc/extract-text.ts +81 -0
- package/src/vendor/zoo-code/src/services/checkpoints/RepoPerTaskCheckpointService.ts +15 -0
- package/src/vendor/zoo-code/src/services/checkpoints/ShadowCheckpointService.ts +553 -0
- package/src/vendor/zoo-code/src/services/checkpoints/excludes.ts +212 -0
- package/src/vendor/zoo-code/src/services/checkpoints/index.ts +3 -0
- package/src/vendor/zoo-code/src/services/checkpoints/types.ts +35 -0
- package/src/vendor/zoo-code/src/services/code-index/manager.ts +19 -0
- package/src/vendor/zoo-code/src/services/mcp/McpHub.ts +36 -0
- package/src/vendor/zoo-code/src/services/roo-config/index.ts +441 -0
- package/src/vendor/zoo-code/src/services/search/file-search.ts +143 -0
- package/src/vendor/zoo-code/src/services/skills/SkillsManager.ts +20 -0
- package/src/vendor/zoo-code/src/shared/globalFileNames.ts +9 -0
- package/src/vendor/zoo-code/src/shared/language.ts +43 -0
- package/src/vendor/zoo-code/src/shared/modes.ts +257 -0
- package/src/vendor/zoo-code/src/shared/tools.ts +385 -0
- package/src/vendor/zoo-code/src/utils/fs.ts +39 -0
- package/src/vendor/zoo-code/src/utils/globalContext.ts +22 -0
- package/src/vendor/zoo-code/src/utils/json-schema.ts +16 -0
- package/src/vendor/zoo-code/src/utils/logging.ts +21 -0
- package/src/vendor/zoo-code/src/utils/mcp-name.ts +190 -0
- package/src/vendor/zoo-code/src/utils/object.ts +18 -0
- package/src/vendor/zoo-code/src/utils/path.ts +94 -0
- package/src/vendor/zoo-code/src/utils/shell.ts +376 -0
- package/src/vendor/zoo-code/src/utils/text-normalization.ts +99 -0
- package/src/vendor/zoo-code/types/global-settings.ts +19 -0
- package/src/vendor/zoo-code/types/index.ts +22 -0
- package/src/vendor/zoo-code/types/message.ts +375 -0
- package/src/vendor/zoo-code/types/mode.ts +241 -0
- package/src/vendor/zoo-code/types/todo.ts +19 -0
- package/src/vendor/zoo-code/types/tool-params.ts +116 -0
- package/src/vendor/zoo-code/types/tool.ts +67 -0
- package/src/vendor/zoo-code/types/vscode.ts +84 -0
- package/src/vision/describe.ts +242 -0
- package/src/vision/tool.ts +91 -0
- package/src/watcher/cli.ts +369 -0
- package/src/watcher/github.ts +304 -0
- package/src/watcher/index.ts +59 -0
- package/src/watcher/state.ts +254 -0
- package/src/watcher/watch.ts +562 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git-aware source-file discovery for the codebase index build.
|
|
3
|
+
*
|
|
4
|
+
* Walks a workspace recursively and returns the relative paths of real source
|
|
5
|
+
* files to index, honoring, in order:
|
|
6
|
+
* 1. `.gitignore` + `.git/info/exclude` (via `git ls-files` / `check-ignore`
|
|
7
|
+
* when the workspace is a git repo; when it isn't, falls back to a
|
|
8
|
+
* minimal built-in exclude list);
|
|
9
|
+
* 2. a hard cap on the number of files (a runaway repo must fail loudly,
|
|
10
|
+
* not silently index forever);
|
|
11
|
+
* 3. an explicit extension allow-list (src/codesearch/types.ts) — binary
|
|
12
|
+
* files and non-source assets are never embedded.
|
|
13
|
+
*
|
|
14
|
+
* `git ls-files --cached --others --exclude-standard` returns every tracked
|
|
15
|
+
* file PLUS every untracked file that is NOT gitignored, with proper
|
|
16
|
+
* .gitignore semantics — strictly better than a hand-rolled ignore matcher,
|
|
17
|
+
* and it reuses the repo's own ignore config the way `list_files` and the
|
|
18
|
+
* vendored checkpoint `excludes.ts` conventions expect (see
|
|
19
|
+
* src/vendor/zoo-code/src/services/checkpoints/excludes.ts for the pattern
|
|
20
|
+
* list those conventions grew from).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { execFile } from "node:child_process"
|
|
24
|
+
import * as fs from "node:fs"
|
|
25
|
+
import * as fsp from "node:fs/promises"
|
|
26
|
+
import * as path from "node:path"
|
|
27
|
+
import { promisify } from "node:util"
|
|
28
|
+
|
|
29
|
+
import { CODE_EXTENSIONS } from "./types.js"
|
|
30
|
+
|
|
31
|
+
const execFileP = promisify(execFile)
|
|
32
|
+
|
|
33
|
+
/** Hard cap on files scanned per index build (mirrors upstream's MAX_LIST_FILES_LIMIT). */
|
|
34
|
+
export const MAX_INDEXED_FILES = 50_000
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generated-artifact directory names skipped EVEN when git-tracked — i.e. even
|
|
38
|
+
* when `.gitignore` doesn't cover them. Real-world repos routinely commit
|
|
39
|
+
* these by accident (a coverage report or build output checked in once and
|
|
40
|
+
* never gitignored); trusting `.gitignore` as the sole source of truth means
|
|
41
|
+
* that mistake silently costs real embedding-API money and index noise
|
|
42
|
+
* forever after. This list is deliberately narrow — only directory names
|
|
43
|
+
* that are near-universally machine-generated and never hand-written source,
|
|
44
|
+
* so it can't plausibly skip something a project actually wants indexed.
|
|
45
|
+
*/
|
|
46
|
+
const ALWAYS_EXCLUDE_DIR_NAMES = new Set(["coverage", "dist", "build", "node_modules", ".next", ".nuxt"])
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generated lockfile basenames skipped even when git-tracked, for the same
|
|
50
|
+
* reason as ALWAYS_EXCLUDE_DIR_NAMES: exact machine-generated dependency
|
|
51
|
+
* manifests with no code-search value, occasionally matched by the
|
|
52
|
+
* extension allow-list (e.g. `package-lock.json`).
|
|
53
|
+
*/
|
|
54
|
+
const ALWAYS_EXCLUDE_FILE_BASENAMES = new Set([
|
|
55
|
+
"package-lock.json",
|
|
56
|
+
"yarn.lock",
|
|
57
|
+
"pnpm-lock.yaml",
|
|
58
|
+
"composer.lock",
|
|
59
|
+
"Gemfile.lock",
|
|
60
|
+
"poetry.lock",
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
/** True when `rel` (a workspace-relative POSIX path) should never be indexed, regardless of .gitignore. */
|
|
64
|
+
function isAlwaysExcluded(rel: string): boolean {
|
|
65
|
+
if (ALWAYS_EXCLUDE_FILE_BASENAMES.has(path.basename(rel))) {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
return rel.split("/").some((segment) => ALWAYS_EXCLUDE_DIR_NAMES.has(segment))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Minimal fallback excludes used when the workspace is NOT a git repo. */
|
|
72
|
+
const FALLBACK_EXCLUDES = new Set([
|
|
73
|
+
".git",
|
|
74
|
+
".gitignore",
|
|
75
|
+
".headlesscode",
|
|
76
|
+
".worktrees",
|
|
77
|
+
".roo",
|
|
78
|
+
"node_modules",
|
|
79
|
+
"dist",
|
|
80
|
+
"build",
|
|
81
|
+
"out",
|
|
82
|
+
"coverage",
|
|
83
|
+
".next",
|
|
84
|
+
".nuxt",
|
|
85
|
+
"vendor",
|
|
86
|
+
"target",
|
|
87
|
+
"__pycache__",
|
|
88
|
+
"venv",
|
|
89
|
+
".venv",
|
|
90
|
+
"env",
|
|
91
|
+
"Pods",
|
|
92
|
+
".idea",
|
|
93
|
+
".vscode",
|
|
94
|
+
".parcel-cache",
|
|
95
|
+
".pytest_cache",
|
|
96
|
+
".terraform",
|
|
97
|
+
".terragrunt-cache",
|
|
98
|
+
"bin",
|
|
99
|
+
"obj",
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
/** A file discovered during the walk. */
|
|
103
|
+
export interface IndexableFile {
|
|
104
|
+
/** Workspace-relative POSIX path. */
|
|
105
|
+
rel: string
|
|
106
|
+
/** Absolute path for reading. */
|
|
107
|
+
abs: string
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function gitLsFiles(root: string): Promise<string[]> {
|
|
111
|
+
try {
|
|
112
|
+
const { stdout } = await execFileP("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
|
|
113
|
+
cwd: root,
|
|
114
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
115
|
+
})
|
|
116
|
+
return stdout
|
|
117
|
+
.split("\n")
|
|
118
|
+
.map((l) => l.trim())
|
|
119
|
+
.filter((l) => l.length > 0)
|
|
120
|
+
} catch {
|
|
121
|
+
return []
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* When the workspace is NOT a git repo (no tracked files), check-ignore can't
|
|
127
|
+
* tell us what's ignored — but a plain `git init`-less directory may still
|
|
128
|
+
* carry a `.gitignore`. We read the root `.gitignore` (plus
|
|
129
|
+
* `.git/info/exclude` if present) and build a tiny matcher from its patterns.
|
|
130
|
+
* This is deliberately a SUBSET of full gitignore semantics (no `**` negation
|
|
131
|
+
* chains, no nested .gitignore files) — good enough for the common "ignore
|
|
132
|
+
* node_modules/.headlesscode/dist" case, and the git-repo path above is the
|
|
133
|
+
* authoritative one.
|
|
134
|
+
*/
|
|
135
|
+
function readRootGitignore(root: string): string[] {
|
|
136
|
+
const patterns: string[] = []
|
|
137
|
+
for (const p of [path.join(root, ".gitignore"), path.join(root, ".git", "info", "exclude")]) {
|
|
138
|
+
try {
|
|
139
|
+
const raw = fs.readFileSync(p, "utf-8")
|
|
140
|
+
for (const line of raw.split("\n")) {
|
|
141
|
+
const trimmed = line.trim()
|
|
142
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
patterns.push(trimmed)
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// missing file — fine
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return patterns
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Match a relative path against a small set of gitignore-style patterns. */
|
|
155
|
+
function matchesIgnorePatterns(rel: string, patterns: string[]): boolean {
|
|
156
|
+
for (const pattern of patterns) {
|
|
157
|
+
// Negation patterns are not supported in the fallback — a lone "!" is
|
|
158
|
+
// treated as an ignore (fail-closed: skip rather than risk indexing
|
|
159
|
+
// something the user meant to exclude).
|
|
160
|
+
if (pattern.startsWith("!")) {
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
const p = pattern.replace(/^\/+/, "").replace(/\/+$/, "")
|
|
164
|
+
if (p === "") {
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
const isDir = p.endsWith("/")
|
|
168
|
+
const base = isDir ? p.slice(0, -1) : p
|
|
169
|
+
// Directory pattern matches any path under a dir of that name.
|
|
170
|
+
if (isDir) {
|
|
171
|
+
if (rel === base || rel.startsWith(base + "/") || rel.split("/").includes(base)) {
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
176
|
+
if (p.startsWith("*")) {
|
|
177
|
+
const suffix = p.slice(1)
|
|
178
|
+
if (rel.endsWith(suffix)) {
|
|
179
|
+
return true
|
|
180
|
+
}
|
|
181
|
+
continue
|
|
182
|
+
}
|
|
183
|
+
// Plain path: match exact relative path or any segment.
|
|
184
|
+
if (rel === p || rel.startsWith(p + "/") || rel.split("/").includes(p)) {
|
|
185
|
+
return true
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Walk the workspace for indexable source files. Returns [] when the
|
|
193
|
+
* workspace doesn't exist or contains nothing indexable.
|
|
194
|
+
*/
|
|
195
|
+
export async function walkSourceFiles(workspaceRoot: string): Promise<IndexableFile[]> {
|
|
196
|
+
const root = path.resolve(workspaceRoot)
|
|
197
|
+
|
|
198
|
+
const gitFiles = await gitLsFiles(root)
|
|
199
|
+
if (gitFiles.length > 0) {
|
|
200
|
+
if (gitFiles.length > MAX_INDEXED_FILES) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`codebase index: workspace has ${gitFiles.length} git-tracked/untracked files — over the ${MAX_INDEXED_FILES} file cap. Refine .gitignore or index a subdirectory.`,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
const out: IndexableFile[] = []
|
|
206
|
+
for (const rel of gitFiles) {
|
|
207
|
+
if (!CODE_EXTENSIONS.has(path.extname(rel).toLowerCase())) {
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
if (isAlwaysExcluded(rel)) {
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
// `git ls-files` reports directories as paths with trailing slashes
|
|
214
|
+
// only in odd edge cases; the extension check above already excludes
|
|
215
|
+
// them. Guard cheaply anyway.
|
|
216
|
+
if (rel.endsWith("/")) {
|
|
217
|
+
continue
|
|
218
|
+
}
|
|
219
|
+
const abs = path.join(root, rel)
|
|
220
|
+
try {
|
|
221
|
+
const st = await fsp.stat(abs)
|
|
222
|
+
if (st.isFile()) {
|
|
223
|
+
out.push({ rel, abs })
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
// File disappeared between git listing and stat — skip.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return out
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Not a git repo: hand-rolled recursive walk with the minimal exclude list
|
|
233
|
+
// PLUS the root .gitignore's own patterns (readRootGitignore + the matcher
|
|
234
|
+
// below) — the git path above is authoritative, but a plain directory with
|
|
235
|
+
// a .gitignore (no git init) must still skip the user's ignored files.
|
|
236
|
+
const patterns = readRootGitignore(root)
|
|
237
|
+
const out: IndexableFile[] = []
|
|
238
|
+
const seen = new Set<string>()
|
|
239
|
+
const walk = async (dir: string): Promise<void> => {
|
|
240
|
+
let entries
|
|
241
|
+
try {
|
|
242
|
+
entries = await fsp.readdir(dir, { withFileTypes: true })
|
|
243
|
+
} catch {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
for (const ent of entries) {
|
|
247
|
+
const abs = path.join(dir, ent.name)
|
|
248
|
+
// No reliance on the vendored String.prototype.toPosix() extension
|
|
249
|
+
// (src/vendor/zoo-code/src/utils/path.js) — this module is also
|
|
250
|
+
// used by the index CLI in isolation, and a plain replace is enough.
|
|
251
|
+
const rel = path.relative(root, abs).replace(/\\/g, "/")
|
|
252
|
+
if (ent.isDirectory()) {
|
|
253
|
+
if (
|
|
254
|
+
FALLBACK_EXCLUDES.has(ent.name) ||
|
|
255
|
+
seen.has(rel) ||
|
|
256
|
+
matchesIgnorePatterns(rel, patterns)
|
|
257
|
+
) {
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
seen.add(rel)
|
|
261
|
+
await walk(abs)
|
|
262
|
+
} else if (ent.isFile()) {
|
|
263
|
+
if (!CODE_EXTENSIONS.has(path.extname(ent.name).toLowerCase())) {
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
if (matchesIgnorePatterns(rel, patterns) || ALWAYS_EXCLUDE_FILE_BASENAMES.has(ent.name)) {
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
269
|
+
out.push({ rel, abs })
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
await walk(root)
|
|
274
|
+
if (out.length > MAX_INDEXED_FILES) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`codebase index: workspace has ${out.length} candidate files — over the ${MAX_INDEXED_FILES} file cap.`,
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
return out
|
|
280
|
+
}
|