ultimate-pi 0.18.0 → 0.19.0
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/.agents/skills/harness-debate-plan/SKILL.md +1 -1
- package/.agents/skills/harness-decisions/SKILL.md +2 -3
- package/.agents/skills/harness-governor/SKILL.md +6 -5
- package/.agents/skills/harness-orchestration/SKILL.md +4 -4
- package/.agents/skills/harness-review/SKILL.md +7 -7
- package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
- package/.agents/skills/harness-steer/SKILL.md +1 -1
- package/.agents/skills/sentrux/SKILL.md +9 -9
- package/.pi/PACKAGING.md +4 -4
- package/.pi/SYSTEM.md +54 -120
- package/.pi/agents/harness/incident-recorder.md +0 -1
- package/.pi/agents/harness/planning/decompose.md +1 -3
- package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
- package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
- package/.pi/agents/harness/planning/hypothesis.md +0 -2
- package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
- package/.pi/agents/harness/planning/plan-adversary.md +0 -2
- package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
- package/.pi/agents/harness/planning/planning-context.md +0 -2
- package/.pi/agents/harness/planning/review-integrator.md +0 -2
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
- package/.pi/agents/harness/planning/stack-researcher.md +0 -2
- package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +0 -2
- package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
- package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
- package/.pi/agents/harness/{executor.md → running/executor.md} +0 -2
- package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
- package/.pi/agents/harness/sentrux-steward.md +0 -2
- package/.pi/agents/harness/trace-librarian.md +0 -1
- package/.pi/extensions/00-harness-project-control.ts +133 -0
- package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
- package/.pi/extensions/agt-kill-switch.ts +57 -0
- package/.pi/extensions/agt-prompt-guard.ts +32 -0
- package/.pi/extensions/budget-guard.ts +2 -0
- package/.pi/extensions/custom-footer.ts +46 -145
- package/.pi/extensions/custom-header.ts +1 -1
- package/.pi/extensions/custom-system-prompt.ts +1 -1
- package/.pi/extensions/debate-orchestrator.ts +7 -5
- package/.pi/extensions/harness-ask-user.ts +8 -8
- package/.pi/extensions/harness-debate-tools.ts +27 -43
- package/.pi/extensions/harness-lens.ts +94 -0
- package/.pi/extensions/harness-live-widget.ts +33 -2
- package/.pi/extensions/harness-plan-approval.ts +12 -12
- package/.pi/extensions/harness-run-context.ts +1214 -852
- package/.pi/extensions/harness-subagent-governance.ts +8 -0
- package/.pi/extensions/harness-subagent-submit.ts +36 -164
- package/.pi/extensions/harness-subagents.ts +4 -4
- package/.pi/extensions/harness-telemetry.ts +3 -1
- package/.pi/extensions/harness-web-tools.ts +3 -3
- package/.pi/extensions/observation-bus.ts +2 -0
- package/.pi/extensions/policy-gate.ts +27 -5
- package/.pi/extensions/review-integrity.ts +91 -10
- package/.pi/extensions/sentrux-rules-sync.ts +3 -1
- package/.pi/extensions/subagent-governance.ts +92 -0
- package/.pi/extensions/test-diff-integrity.ts +1 -0
- package/.pi/extensions/trace-recorder.ts +3 -1
- package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
- package/.pi/harness/README.md +6 -2
- package/.pi/harness/agents.manifest.json +38 -49
- package/.pi/harness/agents.policy.yaml +275 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
- package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
- package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +3 -2
- package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
- package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
- package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
- package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
- package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
- package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
- package/.pi/harness/docs/adrs/README.md +6 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
- package/.pi/harness/docs/practice-map.md +2 -2
- package/.pi/harness/evolution/README.md +1 -2
- package/.pi/harness/examples/agents.policy.project.yaml +19 -0
- package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
- package/.pi/harness/policies/bash-denylists.yaml +5 -0
- package/.pi/harness/policies/defaults.yaml +51 -0
- package/.pi/harness/policies/orchestrator.yaml +18 -0
- package/.pi/harness/policies/phases.yaml +10 -0
- package/.pi/harness/policies/roles.yaml +5 -0
- package/.pi/harness/policies/web-guard.yaml +5 -0
- package/.pi/harness/policies/workflow-sequences.yaml +9 -0
- package/.pi/harness/sentrux/architecture.manifest.json +26 -4
- package/.pi/harness/specs/harness-spawn-context.schema.json +1 -1
- package/.pi/harness/specs/observation.schema.json +2 -1
- package/.pi/lib/agents-policy.d.mts +70 -0
- package/.pi/lib/agents-policy.mjs +325 -0
- package/.pi/lib/agents-policy.ts +19 -0
- package/.pi/lib/agt/audit-run-sink.ts +52 -0
- package/.pi/lib/agt/build-evaluation-context.ts +285 -0
- package/.pi/lib/agt/config.ts +28 -0
- package/.pi/lib/agt/delegation.ts +69 -0
- package/.pi/lib/agt/evaluate-policy.ts +56 -0
- package/.pi/lib/agt/identity-registry.ts +41 -0
- package/.pi/lib/agt/index.ts +55 -0
- package/.pi/lib/agt/kill-switch-state.ts +11 -0
- package/.pi/lib/agt/legacy-evaluate.ts +101 -0
- package/.pi/lib/agt/policy-engine.ts +154 -0
- package/.pi/lib/agt/rings.ts +21 -0
- package/.pi/lib/agt/sre-hooks.ts +45 -0
- package/.pi/lib/agt/trust-run-store.ts +26 -0
- package/.pi/lib/agt/workflow-history.ts +29 -0
- package/.pi/lib/agt-governance-active.ts +14 -0
- package/.pi/lib/agt-tool-guard.ts +78 -0
- package/.pi/lib/ask-user/dialog.ts +314 -0
- package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
- package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
- package/.pi/{extensions/lib → lib}/extension-load-guard.ts +21 -0
- package/.pi/lib/harness-agt-tool-guard.ts +5 -0
- package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
- package/.pi/lib/harness-debate-core-deps.ts +14 -0
- package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
- package/.pi/lib/harness-lens/.gitattributes +1 -0
- package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
- package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
- package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
- package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
- package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
- package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
- package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
- package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
- package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
- package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
- package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
- package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
- package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
- package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
- package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
- package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
- package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
- package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
- package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
- package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
- package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
- package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
- package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
- package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
- package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
- package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
- package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
- package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
- package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
- package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
- package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
- package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
- package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
- package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
- package/.pi/lib/harness-lens/clients/types.ts +59 -0
- package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
- package/.pi/lib/harness-lens/index.ts +532 -0
- package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
- package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
- package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
- package/.pi/lib/harness-project-config.ts +91 -0
- package/.pi/lib/harness-run-context-responses.ts +9 -0
- package/.pi/lib/harness-run-context.ts +1 -3
- package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
- package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
- package/.pi/lib/harness-subagent-auth.ts +51 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
- package/.pi/lib/harness-subagent-submit-register.ts +163 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -55
- package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
- package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
- package/.pi/lib/harness-ui-state.ts +27 -12
- package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
- package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
- package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +3 -52
- package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
- package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
- package/.pi/prompts/harness-auto.md +2 -2
- package/.pi/prompts/harness-plan.md +4 -6
- package/.pi/prompts/harness-review.md +9 -9
- package/.pi/prompts/harness-run.md +7 -7
- package/.pi/prompts/harness-setup.md +42 -68
- package/.pi/prompts/harness-steer.md +2 -2
- package/.pi/scripts/README.md +3 -5
- package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
- package/.pi/scripts/graphify-kb-updater.mjs +48 -8
- package/.pi/scripts/harness-agents-manifest.mjs +61 -4
- package/.pi/scripts/harness-agt-doctor.ts +36 -0
- package/.pi/scripts/harness-cli-verify.sh +9 -2
- package/.pi/scripts/harness-project-toggle.mjs +129 -0
- package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
- package/.pi/scripts/harness-verify.mjs +113 -39
- package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
- package/.pi/scripts/validate-plan-dag.mjs +65 -74
- package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
- package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
- package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
- package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
- package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
- package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
- package/.pi/skills/architecture/layered/SKILL.md +68 -0
- package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
- package/.pi/skills/architecture/microservices/SKILL.md +64 -0
- package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
- package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
- package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
- package/.pi/skills/architecture/service-based/SKILL.md +64 -0
- package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
- package/.pi/skills/architecture/space-based/SKILL.md +60 -0
- package/.pi/skills/ast-grep/SKILL.md +40 -321
- package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
- package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
- package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
- package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
- package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
- package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
- package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
- package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
- package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
- package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
- package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
- package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
- package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
- package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
- package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
- package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
- package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
- package/.pi/skills/lsp-navigation/SKILL.md +89 -0
- package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
- package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
- package/.pi/skills/quality/security-review/SKILL.md +34 -0
- package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
- package/.pi/skills/quality/testability-design/SKILL.md +33 -0
- package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
- package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
- package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
- package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
- package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
- package/.sentrux/rules.toml +20 -4
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +26 -0
- package/README.md +85 -58
- package/THIRD_PARTY_NOTICES.md +12 -21
- package/package.json +15 -7
- package/vendor/pi-subagents/src/agents.ts +45 -1
- package/vendor/pi-subagents/src/subagents.ts +866 -811
- package/vendor/pi-vcc/src/core/brief.ts +68 -99
- package/vendor/pi-vcc/src/core/settings.ts +2 -2
- package/.agents/skills/caveman/SKILL.md +0 -67
- package/.pi/agents/harness/meta-optimizer.md +0 -36
- package/.pi/agents/harness/planning/scout-graphify.md +0 -39
- package/.pi/agents/harness/planning/scout-semantic.md +0 -41
- package/.pi/agents/harness/planning/scout-structure.md +0 -37
- package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
- package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
- package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
- package/.pi/extensions/pi-model-router-harness.ts +0 -42
- package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
- package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
- package/.pi/model-router.example.json +0 -36
- package/.pi/prompts/harness-critic.md +0 -10
- package/.pi/prompts/harness-eval.md +0 -10
- package/.pi/prompts/harness-router-tune.md +0 -52
- package/.pi/scripts/harness-generate-model-router.mjs +0 -327
- package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
- package/.pi/scripts/harness-sync-model-router.mjs +0 -97
- package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
- package/vendor/pi-model-router/.prettierignore +0 -4
- package/vendor/pi-model-router/.prettierrc +0 -5
- package/vendor/pi-model-router/AGENTS.md +0 -39
- package/vendor/pi-model-router/LICENSE +0 -21
- package/vendor/pi-model-router/README.md +0 -99
- package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
- package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
- package/vendor/pi-model-router/extensions/commands.ts +0 -720
- package/vendor/pi-model-router/extensions/config.ts +0 -348
- package/vendor/pi-model-router/extensions/constants.ts +0 -1
- package/vendor/pi-model-router/extensions/index.ts +0 -478
- package/vendor/pi-model-router/extensions/provider.ts +0 -580
- package/vendor/pi-model-router/extensions/routing.ts +0 -564
- package/vendor/pi-model-router/extensions/state.ts +0 -52
- package/vendor/pi-model-router/extensions/types.ts +0 -95
- package/vendor/pi-model-router/extensions/ui.ts +0 -144
- package/vendor/pi-model-router/model-router.example.json +0 -48
- package/vendor/pi-model-router/package.json +0 -48
- package/vendor/pi-model-router/tsconfig.json +0 -16
- /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared file path utilities for pi-lens
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { minimatch } from "minimatch";
|
|
8
|
+
import { normalizeFilePath } from "./path-utils.js";
|
|
9
|
+
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Return the directory where pi-lens stores project-specific data
|
|
13
|
+
* (caches, indexes, worklogs, etc.).
|
|
14
|
+
*
|
|
15
|
+
* Default: store under <project>/.pi/harness/.lens/projects/<project-slug>.
|
|
16
|
+
*
|
|
17
|
+
* Override: set PILENS_DATA_DIR=/some/path — each project gets its own
|
|
18
|
+
* subdirectory named after a sanitized form of its absolute path, e.g.
|
|
19
|
+
* PILENS_DATA_DIR=.pi/harness/.lens/projects
|
|
20
|
+
* → .pi/harness/.lens/projects/home-user-myapp/
|
|
21
|
+
*/
|
|
22
|
+
export function getProjectDataDir(cwd: string): string {
|
|
23
|
+
const configuredBase = process.env.PILENS_DATA_DIR?.trim();
|
|
24
|
+
const base =
|
|
25
|
+
configuredBase ||
|
|
26
|
+
path.join(
|
|
27
|
+
path.resolve(cwd || process.cwd()),
|
|
28
|
+
".pi",
|
|
29
|
+
"harness",
|
|
30
|
+
".lens",
|
|
31
|
+
"projects",
|
|
32
|
+
);
|
|
33
|
+
const normalized = normalizeFilePath(path.resolve(cwd));
|
|
34
|
+
const slug = normalized
|
|
35
|
+
.replace(/^[a-z]:/i, "") // strip Windows drive letter
|
|
36
|
+
.replace(/\/+/g, "-") // separators → dashes
|
|
37
|
+
.replace(/[^A-Za-z0-9-]/g, "") // strip anything else
|
|
38
|
+
.replace(/^-+/, "") // trim leading dashes
|
|
39
|
+
.replace(/-+$/, ""); // trim trailing dashes
|
|
40
|
+
return path.join(base.trim(), slug || "default");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Directories to exclude from all scans (build outputs, dependencies, caches).
|
|
45
|
+
* Used consistently across all scanners to avoid noise from generated files.
|
|
46
|
+
*/
|
|
47
|
+
export const EXCLUDED_DIRS = [
|
|
48
|
+
"node_modules",
|
|
49
|
+
".git",
|
|
50
|
+
"dist",
|
|
51
|
+
"build",
|
|
52
|
+
".turbo",
|
|
53
|
+
".cache",
|
|
54
|
+
"target",
|
|
55
|
+
"out",
|
|
56
|
+
".parcel-cache",
|
|
57
|
+
".svelte-kit",
|
|
58
|
+
".nuxt",
|
|
59
|
+
".yarn",
|
|
60
|
+
".pnpm-store",
|
|
61
|
+
".gradle",
|
|
62
|
+
".next",
|
|
63
|
+
".pi", // pi agent directory, including harness lens runtime data
|
|
64
|
+
".ruff_cache", // Python linter cache
|
|
65
|
+
".worktrees",
|
|
66
|
+
".claude",
|
|
67
|
+
".codex",
|
|
68
|
+
".rescue",
|
|
69
|
+
".agents",
|
|
70
|
+
".gstack",
|
|
71
|
+
".superpowers",
|
|
72
|
+
".guardrails",
|
|
73
|
+
".playwright-cli",
|
|
74
|
+
".playwright-mcp",
|
|
75
|
+
".vscode",
|
|
76
|
+
"venv",
|
|
77
|
+
".venv",
|
|
78
|
+
"coverage",
|
|
79
|
+
"__pycache__",
|
|
80
|
+
".tox",
|
|
81
|
+
".pytest_cache",
|
|
82
|
+
"*.dSYM",
|
|
83
|
+
// Vendored upstream source conventions — universally too large to scan
|
|
84
|
+
"vendor", // Go modules, PHP Composer, Ruby Bundler
|
|
85
|
+
"third_party", // Chromium/Google convention (llama.cpp, sherpa-onnx, gRPC, TF)
|
|
86
|
+
"third-party",
|
|
87
|
+
"vendors",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
export interface GitignorePattern {
|
|
91
|
+
pattern: string;
|
|
92
|
+
negated: boolean;
|
|
93
|
+
directoryOnly: boolean;
|
|
94
|
+
rooted: boolean;
|
|
95
|
+
hasSlash: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ProjectIgnoreMatcher {
|
|
99
|
+
rootDir: string;
|
|
100
|
+
patterns: GitignorePattern[];
|
|
101
|
+
isIgnored(filePath: string, isDirectory?: boolean): boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function resolveGitIgnoreRoot(startDir: string): string {
|
|
105
|
+
const fallback = path.resolve(startDir);
|
|
106
|
+
let current = fallback;
|
|
107
|
+
while (true) {
|
|
108
|
+
if (fs.existsSync(path.join(current, ".git"))) return current;
|
|
109
|
+
const parent = path.dirname(current);
|
|
110
|
+
if (parent === current) return fallback;
|
|
111
|
+
current = parent;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collapseSlashes(value: string): string {
|
|
116
|
+
let out = "";
|
|
117
|
+
let previousWasSlash = false;
|
|
118
|
+
for (const ch of value) {
|
|
119
|
+
if (ch === "/") {
|
|
120
|
+
if (!previousWasSlash) out += ch;
|
|
121
|
+
previousWasSlash = true;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
out += ch === "\\" ? "/" : ch;
|
|
125
|
+
previousWasSlash = false;
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function stripLeadingDotSlash(value: string): string {
|
|
131
|
+
return value.startsWith("./") ? value.slice(2) : value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function stripTrailingSlashes(value: string): string {
|
|
135
|
+
let end = value.length;
|
|
136
|
+
while (end > 0 && value[end - 1] === "/") end -= 1;
|
|
137
|
+
return value.slice(0, end);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function stripLeadingSlashes(value: string): string {
|
|
141
|
+
let start = 0;
|
|
142
|
+
while (start < value.length && value[start] === "/") start += 1;
|
|
143
|
+
return value.slice(start);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function normalizeIgnorePath(value: string): string {
|
|
147
|
+
return collapseSlashes(stripLeadingDotSlash(value));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stripTrailingSpaces(value: string): string {
|
|
151
|
+
// Good-enough gitignore whitespace handling: unescaped trailing spaces are ignored.
|
|
152
|
+
let end = value.length;
|
|
153
|
+
while (end > 0 && value[end - 1] === " " && value[end - 2] !== "\\") end -= 1;
|
|
154
|
+
return value.slice(0, end).replace(/\\ /g, " ");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function parseGitignoreContent(content: string): GitignorePattern[] {
|
|
158
|
+
const patterns: GitignorePattern[] = [];
|
|
159
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
160
|
+
let line = stripTrailingSpaces(rawLine.trimStart());
|
|
161
|
+
if (!line || line.startsWith("#")) continue;
|
|
162
|
+
let negated = false;
|
|
163
|
+
if (line.startsWith("!")) {
|
|
164
|
+
negated = true;
|
|
165
|
+
line = line.slice(1);
|
|
166
|
+
}
|
|
167
|
+
line = normalizeIgnorePath(line);
|
|
168
|
+
if (!line) continue;
|
|
169
|
+
|
|
170
|
+
const directoryOnly = line.endsWith("/");
|
|
171
|
+
if (directoryOnly) line = stripTrailingSlashes(line);
|
|
172
|
+
const rooted = line.startsWith("/");
|
|
173
|
+
if (rooted) line = stripLeadingSlashes(line);
|
|
174
|
+
if (!line) continue;
|
|
175
|
+
|
|
176
|
+
patterns.push({
|
|
177
|
+
pattern: line,
|
|
178
|
+
negated,
|
|
179
|
+
directoryOnly,
|
|
180
|
+
rooted,
|
|
181
|
+
hasSlash: line.includes("/"),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return patterns;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function expandGitignorePattern(pattern: GitignorePattern): string[] {
|
|
188
|
+
const body = pattern.pattern;
|
|
189
|
+
if (pattern.directoryOnly) {
|
|
190
|
+
if (pattern.rooted || pattern.hasSlash) return [body, `${body}/**`];
|
|
191
|
+
return [body, `${body}/**`, `**/${body}`, `**/${body}/**`];
|
|
192
|
+
}
|
|
193
|
+
if (pattern.rooted || pattern.hasSlash) return [body];
|
|
194
|
+
return [body, `**/${body}`];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function matchesGitignorePattern(
|
|
198
|
+
pattern: GitignorePattern,
|
|
199
|
+
relativePath: string,
|
|
200
|
+
isDirectory: boolean,
|
|
201
|
+
): boolean {
|
|
202
|
+
const candidate = stripLeadingSlashes(normalizeIgnorePath(relativePath));
|
|
203
|
+
if (!candidate) return false;
|
|
204
|
+
const candidates = isDirectory ? [candidate, `${candidate}/`] : [candidate];
|
|
205
|
+
const options = { dot: true, nocase: process.platform === "win32" };
|
|
206
|
+
return expandGitignorePattern(pattern).some((expanded) => {
|
|
207
|
+
if (isDirectory && expanded.endsWith("/**")) {
|
|
208
|
+
const prefix = expanded.slice(0, -3);
|
|
209
|
+
if (candidate === prefix || candidate.startsWith(`${prefix}/`))
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
return candidates.some((value) => minimatch(value, expanded, options));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function readGitignorePatterns(rootDir: string): GitignorePattern[] {
|
|
217
|
+
const gitignorePath = path.join(rootDir, ".gitignore");
|
|
218
|
+
try {
|
|
219
|
+
return parseGitignoreContent(fs.readFileSync(gitignorePath, "utf-8"));
|
|
220
|
+
} catch {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function ancestorDirsBetween(rootDir: string, targetDir: string): string[] {
|
|
226
|
+
const relative = path.relative(rootDir, targetDir);
|
|
227
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) return [];
|
|
228
|
+
const dirs = [rootDir];
|
|
229
|
+
if (!relative) return dirs;
|
|
230
|
+
let current = rootDir;
|
|
231
|
+
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
232
|
+
current = path.join(current, segment);
|
|
233
|
+
dirs.push(current);
|
|
234
|
+
}
|
|
235
|
+
return dirs;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function buildProjectIgnoreMatcher(
|
|
239
|
+
resolvedRoot: string,
|
|
240
|
+
patterns: GitignorePattern[],
|
|
241
|
+
): ProjectIgnoreMatcher {
|
|
242
|
+
const nestedCache = new Map<
|
|
243
|
+
string,
|
|
244
|
+
{ gitignoreMtimeMs: number; patterns: GitignorePattern[] }
|
|
245
|
+
>();
|
|
246
|
+
const patternsForDir = (dir: string): GitignorePattern[] => {
|
|
247
|
+
if (dir === resolvedRoot) return patterns;
|
|
248
|
+
const gitignoreMtime = gitignoreMtimeMs(dir);
|
|
249
|
+
const cached = nestedCache.get(dir);
|
|
250
|
+
if (cached?.gitignoreMtimeMs === gitignoreMtime) return cached.patterns;
|
|
251
|
+
const nextPatterns = readGitignorePatterns(dir);
|
|
252
|
+
nestedCache.set(dir, {
|
|
253
|
+
gitignoreMtimeMs: gitignoreMtime,
|
|
254
|
+
patterns: nextPatterns,
|
|
255
|
+
});
|
|
256
|
+
return nextPatterns;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
rootDir: resolvedRoot,
|
|
261
|
+
patterns,
|
|
262
|
+
isIgnored(filePath: string, isDirectory = false): boolean {
|
|
263
|
+
const resolved = path.resolve(filePath);
|
|
264
|
+
const rootRelative = path.relative(resolvedRoot, resolved);
|
|
265
|
+
if (
|
|
266
|
+
!rootRelative ||
|
|
267
|
+
rootRelative.startsWith("..") ||
|
|
268
|
+
path.isAbsolute(rootRelative)
|
|
269
|
+
) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
let ignored = false;
|
|
274
|
+
const patternDirs = ancestorDirsBetween(
|
|
275
|
+
resolvedRoot,
|
|
276
|
+
path.dirname(resolved),
|
|
277
|
+
);
|
|
278
|
+
for (const dir of patternDirs) {
|
|
279
|
+
const dirPatterns = patternsForDir(dir);
|
|
280
|
+
if (dirPatterns.length === 0) continue;
|
|
281
|
+
const relative = path.relative(dir, resolved);
|
|
282
|
+
const normalized = normalizeIgnorePath(relative);
|
|
283
|
+
for (const pattern of dirPatterns) {
|
|
284
|
+
if (!matchesGitignorePattern(pattern, normalized, isDirectory))
|
|
285
|
+
continue;
|
|
286
|
+
ignored = !pattern.negated;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return ignored;
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function createProjectIgnoreMatcher(
|
|
295
|
+
rootDir: string,
|
|
296
|
+
extraPatterns: string[] = [],
|
|
297
|
+
): ProjectIgnoreMatcher {
|
|
298
|
+
const resolvedRoot = resolveGitIgnoreRoot(rootDir);
|
|
299
|
+
const patterns = [
|
|
300
|
+
...readGitignorePatterns(resolvedRoot),
|
|
301
|
+
...parseGitignoreContent(extraPatterns.join("\n")),
|
|
302
|
+
];
|
|
303
|
+
return buildProjectIgnoreMatcher(resolvedRoot, patterns);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const projectIgnoreMatcherCache = new Map<
|
|
307
|
+
string,
|
|
308
|
+
{ gitignoreMtimeMs: number; matcher: ProjectIgnoreMatcher }
|
|
309
|
+
>();
|
|
310
|
+
|
|
311
|
+
function gitignoreMtimeMs(rootDir: string): number {
|
|
312
|
+
try {
|
|
313
|
+
return fs.statSync(path.join(rootDir, ".gitignore")).mtimeMs;
|
|
314
|
+
} catch {
|
|
315
|
+
return -1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function getProjectIgnoreMatcher(rootDir: string): ProjectIgnoreMatcher {
|
|
320
|
+
const resolvedRoot = resolveGitIgnoreRoot(rootDir);
|
|
321
|
+
const gitignoreMtime = gitignoreMtimeMs(resolvedRoot);
|
|
322
|
+
const cached = projectIgnoreMatcherCache.get(resolvedRoot);
|
|
323
|
+
if (cached?.gitignoreMtimeMs === gitignoreMtime) return cached.matcher;
|
|
324
|
+
|
|
325
|
+
const matcher = createProjectIgnoreMatcher(resolvedRoot);
|
|
326
|
+
projectIgnoreMatcherCache.set(resolvedRoot, {
|
|
327
|
+
gitignoreMtimeMs: gitignoreMtime,
|
|
328
|
+
matcher,
|
|
329
|
+
});
|
|
330
|
+
return matcher;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function isPathIgnoredByProject(
|
|
334
|
+
filePath: string,
|
|
335
|
+
rootDir: string,
|
|
336
|
+
isDirectory = false,
|
|
337
|
+
): boolean {
|
|
338
|
+
return getProjectIgnoreMatcher(rootDir).isIgnored(filePath, isDirectory);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function getProjectIgnoreGlobs(rootDir: string): string[] {
|
|
342
|
+
return readGitignorePatterns(rootDir)
|
|
343
|
+
.filter((pattern) => !pattern.negated)
|
|
344
|
+
.flatMap((pattern) => expandGitignorePattern(pattern));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Read simple directory-name entries from a root .gitignore.
|
|
349
|
+
*
|
|
350
|
+
* Prefer createProjectIgnoreMatcher() for path-aware gitignore matching. This
|
|
351
|
+
* helper is kept for callers/tests that only need simple directory names.
|
|
352
|
+
*/
|
|
353
|
+
export function readGitignoreDirs(rootDir: string): string[] {
|
|
354
|
+
return readGitignorePatterns(rootDir)
|
|
355
|
+
.filter(
|
|
356
|
+
(entry) =>
|
|
357
|
+
!entry.negated &&
|
|
358
|
+
!entry.pattern.includes("*") &&
|
|
359
|
+
!entry.pattern.includes("?") &&
|
|
360
|
+
!entry.pattern.includes("[") &&
|
|
361
|
+
!entry.pattern.includes("/"),
|
|
362
|
+
)
|
|
363
|
+
.map((entry) => entry.pattern);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function globToRegExp(glob: string): RegExp {
|
|
367
|
+
const escaped = glob
|
|
368
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
369
|
+
.replace(/\*/g, ".*")
|
|
370
|
+
.replace(/\?/g, ".");
|
|
371
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Match directory name against exclusion patterns.
|
|
376
|
+
* Supports exact names and lightweight glob patterns (for example `*.dSYM`).
|
|
377
|
+
*/
|
|
378
|
+
export function isExcludedDirName(
|
|
379
|
+
dirName: string,
|
|
380
|
+
extraPatterns: string[] = [],
|
|
381
|
+
): boolean {
|
|
382
|
+
const candidate = dirName.trim();
|
|
383
|
+
if (!candidate) return false;
|
|
384
|
+
|
|
385
|
+
const patterns = [...EXCLUDED_DIRS, ...extraPatterns]
|
|
386
|
+
.map((p) => p.trim())
|
|
387
|
+
.filter((p) => p.length > 0);
|
|
388
|
+
const candidateLower = candidate.toLowerCase();
|
|
389
|
+
|
|
390
|
+
for (const pattern of patterns) {
|
|
391
|
+
const patLower = pattern.toLowerCase();
|
|
392
|
+
if (!patLower.includes("*") && !patLower.includes("?")) {
|
|
393
|
+
if (candidateLower === patLower) return true;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (globToRegExp(pattern).test(candidate)) return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Convert excluded directory names into glob patterns used by scanners.
|
|
404
|
+
*/
|
|
405
|
+
export function getExcludedDirGlobs(): string[] {
|
|
406
|
+
return EXCLUDED_DIRS.map((dir) => `**/${dir}/**`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Shared Knip ignore patterns derived from central exclusions.
|
|
411
|
+
*/
|
|
412
|
+
export function getKnipIgnorePatterns(): string[] {
|
|
413
|
+
return [
|
|
414
|
+
...getExcludedDirGlobs(),
|
|
415
|
+
"**/*.test.ts",
|
|
416
|
+
"**/*.test.tsx",
|
|
417
|
+
"**/*.test.js",
|
|
418
|
+
"**/*.test.jsx",
|
|
419
|
+
"**/*.spec.ts",
|
|
420
|
+
"**/*.spec.tsx",
|
|
421
|
+
"**/*.spec.js",
|
|
422
|
+
"**/*.spec.jsx",
|
|
423
|
+
"**/*.poc.test.ts",
|
|
424
|
+
"**/*.poc.test.tsx",
|
|
425
|
+
"**/__tests__/**",
|
|
426
|
+
"**/tests/**",
|
|
427
|
+
];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Spawn a command and detect whether it modified a file on disk.
|
|
432
|
+
* Returns 1 if the file content changed after the command ran, 0 otherwise.
|
|
433
|
+
* Useful for auto-fix tools (ESLint, Stylelint, RuboCop, etc.).
|
|
434
|
+
*/
|
|
435
|
+
export async function detectFileChangedAfterCommand(
|
|
436
|
+
filePath: string,
|
|
437
|
+
command: string,
|
|
438
|
+
args: string[],
|
|
439
|
+
cwd: string,
|
|
440
|
+
ignoreStatuses: number[] = [],
|
|
441
|
+
): Promise<number> {
|
|
442
|
+
let before = "";
|
|
443
|
+
try {
|
|
444
|
+
before = fs.readFileSync(filePath, "utf-8");
|
|
445
|
+
} catch {
|
|
446
|
+
return 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const result = await safeSpawnAsync(command, args, {
|
|
450
|
+
timeout: 30000,
|
|
451
|
+
cwd,
|
|
452
|
+
});
|
|
453
|
+
if (result.error) return 0;
|
|
454
|
+
if (result.status !== 0 && !ignoreStatuses.includes(result.status ?? -1)) {
|
|
455
|
+
return 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const after = fs.readFileSync(filePath, "utf-8");
|
|
460
|
+
return before !== after ? 1 : 0;
|
|
461
|
+
} catch {
|
|
462
|
+
return 0;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Check if file path is a test/fixture/mock file.
|
|
468
|
+
* Used by secrets scanner, rate command, and dispatch runners
|
|
469
|
+
* to skip these files (false positives on fake credentials, etc).
|
|
470
|
+
*/
|
|
471
|
+
export function isTestFile(filePath: string): boolean {
|
|
472
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
473
|
+
return (
|
|
474
|
+
normalized.includes(".test.") ||
|
|
475
|
+
normalized.includes(".spec.") ||
|
|
476
|
+
normalized.includes("/test/") ||
|
|
477
|
+
normalized.includes("/tests/") ||
|
|
478
|
+
normalized.includes("__tests__/") ||
|
|
479
|
+
normalized.includes("test-utils") ||
|
|
480
|
+
normalized.startsWith("test-") ||
|
|
481
|
+
normalized.includes(".fixture.") ||
|
|
482
|
+
normalized.includes(".mock.")
|
|
483
|
+
);
|
|
484
|
+
}
|