ultimate-pi 0.18.1 → 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 +1 -2
- package/.agents/skills/harness-governor/SKILL.md +6 -5
- 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 +0 -2
- 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/reviewing/adversary.md +0 -2
- package/.pi/agents/harness/reviewing/evaluator.md +0 -2
- package/.pi/agents/harness/reviewing/tie-breaker.md +0 -2
- package/.pi/agents/harness/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-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/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 +6 -6
- package/.pi/extensions/harness-ask-user.ts +7 -7
- package/.pi/extensions/harness-debate-tools.ts +26 -42
- package/.pi/extensions/harness-lens.ts +94 -0
- package/.pi/extensions/harness-plan-approval.ts +11 -11
- package/.pi/extensions/harness-run-context.ts +1070 -876
- package/.pi/extensions/harness-subagent-governance.ts +8 -0
- package/.pi/extensions/harness-subagent-submit.ts +34 -163
- package/.pi/extensions/harness-subagents.ts +3 -3
- package/.pi/extensions/harness-telemetry.ts +2 -2
- package/.pi/extensions/harness-web-tools.ts +2 -2
- package/.pi/extensions/policy-gate.ts +25 -5
- package/.pi/extensions/sentrux-rules-sync.ts +1 -1
- package/.pi/extensions/subagent-governance.ts +92 -0
- package/.pi/extensions/trace-recorder.ts +1 -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 +22 -25
- package/.pi/harness/agents.policy.yaml +275 -0
- 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/0045-harness-lens-minimal-contract.md +49 -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 +5 -0
- 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/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 +13 -2
- package/.pi/lib/harness-agt-tool-guard.ts +5 -0
- package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +1 -1
- 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-run-context-responses.ts +9 -0
- package/.pi/lib/harness-run-context.ts +0 -2
- package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +1 -0
- package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +1 -1
- package/.pi/lib/harness-subagent-auth.ts +51 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +10 -7
- 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 -37
- package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
- package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
- 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-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-plan.md +1 -1
- package/.pi/prompts/harness-setup.md +37 -64
- package/.pi/scripts/README.md +2 -5
- package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
- package/.pi/scripts/harness-agents-manifest.mjs +60 -3
- package/.pi/scripts/harness-agt-doctor.ts +36 -0
- package/.pi/scripts/harness-cli-verify.sh +9 -2
- 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 +14 -0
- package/README.md +3 -12
- 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/extensions/lib/ask-user/dialog.ts +0 -260
- package/.pi/extensions/lib/harness-subagent-auth.ts +0 -207
- 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-approval-readiness.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,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Service for pi-lens
|
|
3
|
+
*
|
|
4
|
+
* Concurrent formatter execution using Effect-TS.
|
|
5
|
+
* Auto-formats files on write with a single selected formatter per file.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Chooses one formatter per file using config-gated or smart-default policy
|
|
9
|
+
* - Runs formatting with timeout protection
|
|
10
|
+
* - FileTime integration for safety
|
|
11
|
+
* - Explicit config wins; otherwise smart defaults apply
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { FileTime } from "./file-time.js";
|
|
16
|
+
import {
|
|
17
|
+
clearFormatterRuntimeState,
|
|
18
|
+
type FormatterInfo,
|
|
19
|
+
type FormatterResult,
|
|
20
|
+
formatFile,
|
|
21
|
+
getFormattersForFile,
|
|
22
|
+
} from "./formatters.js";
|
|
23
|
+
import { recordFormatter } from "./widget-state.js";
|
|
24
|
+
|
|
25
|
+
// --- Configuration ---
|
|
26
|
+
|
|
27
|
+
/** Reserved for future batching; formatting currently runs one selected formatter per file. */
|
|
28
|
+
const DEFAULT_FORMATTER_CONCURRENCY = 1;
|
|
29
|
+
|
|
30
|
+
// --- Types ---
|
|
31
|
+
|
|
32
|
+
export interface FormatOptions {
|
|
33
|
+
/** Skip auto-format even if enabled (manual mode) */
|
|
34
|
+
skip?: boolean;
|
|
35
|
+
/** Specific formatters to use (overrides detection) */
|
|
36
|
+
formatters?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FormatSummary {
|
|
40
|
+
filePath: string;
|
|
41
|
+
formatters: Array<{
|
|
42
|
+
name: string;
|
|
43
|
+
success: boolean;
|
|
44
|
+
changed: boolean;
|
|
45
|
+
error?: string;
|
|
46
|
+
}>;
|
|
47
|
+
anyChanged: boolean;
|
|
48
|
+
allSucceeded: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --- Format Service ---
|
|
52
|
+
|
|
53
|
+
export class FormatService {
|
|
54
|
+
private fileTime: FileTime;
|
|
55
|
+
private enabled: boolean;
|
|
56
|
+
|
|
57
|
+
constructor(sessionID: string, enabled: boolean = true) {
|
|
58
|
+
this.fileTime = new FileTime(sessionID);
|
|
59
|
+
this.enabled = enabled;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Format a file with the single selected formatter for that file.
|
|
64
|
+
*/
|
|
65
|
+
async formatFile(
|
|
66
|
+
filePath: string,
|
|
67
|
+
options: FormatOptions = {},
|
|
68
|
+
): Promise<FormatSummary> {
|
|
69
|
+
const absolutePath = path.resolve(filePath);
|
|
70
|
+
const cwd = path.dirname(absolutePath);
|
|
71
|
+
|
|
72
|
+
// Skip if disabled
|
|
73
|
+
if (options.skip || !this.enabled) {
|
|
74
|
+
return {
|
|
75
|
+
filePath: absolutePath,
|
|
76
|
+
formatters: [],
|
|
77
|
+
anyChanged: false,
|
|
78
|
+
allSucceeded: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if file was modified externally (safety check)
|
|
83
|
+
if (this.fileTime.hasChanged(absolutePath)) {
|
|
84
|
+
console.warn(
|
|
85
|
+
`[format] File ${absolutePath} modified externally, skipping format`,
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
filePath: absolutePath,
|
|
89
|
+
formatters: [],
|
|
90
|
+
anyChanged: false,
|
|
91
|
+
allSucceeded: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Get formatters for this file
|
|
96
|
+
const formatters = options.formatters
|
|
97
|
+
? await this.getFormattersByName(options.formatters)
|
|
98
|
+
: await getFormattersForFile(absolutePath, cwd);
|
|
99
|
+
|
|
100
|
+
if (formatters.length === 0) {
|
|
101
|
+
return {
|
|
102
|
+
filePath: absolutePath,
|
|
103
|
+
formatters: [],
|
|
104
|
+
anyChanged: false,
|
|
105
|
+
allSucceeded: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Run formatters with limited concurrency
|
|
110
|
+
const results = await this.runFormattersWithConcurrency(
|
|
111
|
+
absolutePath,
|
|
112
|
+
formatters,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Record new file state after formatting
|
|
116
|
+
this.fileTime.read(absolutePath);
|
|
117
|
+
|
|
118
|
+
for (const [index, result] of results.entries()) {
|
|
119
|
+
recordFormatter(
|
|
120
|
+
absolutePath,
|
|
121
|
+
formatters[index]?.name ?? "unknown",
|
|
122
|
+
result.changed,
|
|
123
|
+
result.success,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Build summary
|
|
128
|
+
const anyChanged = results.some((r) => r.changed);
|
|
129
|
+
const allSucceeded = results.every((r) => r.success);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
filePath: absolutePath,
|
|
133
|
+
formatters: results.map((r, i) => ({
|
|
134
|
+
name: formatters[i].name,
|
|
135
|
+
success: r.success,
|
|
136
|
+
changed: r.changed,
|
|
137
|
+
error: r.error,
|
|
138
|
+
})),
|
|
139
|
+
anyChanged,
|
|
140
|
+
allSucceeded,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Run the selected formatter with timeout protection.
|
|
146
|
+
*/
|
|
147
|
+
private async runFormattersWithConcurrency(
|
|
148
|
+
filePath: string,
|
|
149
|
+
formatters: FormatterInfo[],
|
|
150
|
+
_concurrency = DEFAULT_FORMATTER_CONCURRENCY,
|
|
151
|
+
): Promise<FormatterResult[]> {
|
|
152
|
+
const results: FormatterResult[] = [];
|
|
153
|
+
|
|
154
|
+
for (const formatter of formatters) {
|
|
155
|
+
try {
|
|
156
|
+
const timeoutMs = 30000;
|
|
157
|
+
const timeoutPromise = new Promise<FormatterResult>((_, reject) => {
|
|
158
|
+
setTimeout(
|
|
159
|
+
() =>
|
|
160
|
+
reject(
|
|
161
|
+
new Error(
|
|
162
|
+
`Formatter ${formatter.name} timed out after ${timeoutMs}ms`,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
timeoutMs,
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const result = await Promise.race([
|
|
170
|
+
formatFile(filePath, formatter),
|
|
171
|
+
timeoutPromise,
|
|
172
|
+
]);
|
|
173
|
+
results.push(result);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
results.push({
|
|
176
|
+
success: false,
|
|
177
|
+
changed: false,
|
|
178
|
+
error: error instanceof Error ? error.message : String(error),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return results;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get formatters by name (for explicit formatter selection)
|
|
188
|
+
*/
|
|
189
|
+
private async getFormattersByName(names: string[]): Promise<FormatterInfo[]> {
|
|
190
|
+
const { listAllFormatters, ...formatters } = await import(
|
|
191
|
+
"./formatters.js"
|
|
192
|
+
);
|
|
193
|
+
const allNames = listAllFormatters();
|
|
194
|
+
|
|
195
|
+
return names
|
|
196
|
+
.filter((name) => allNames.includes(name))
|
|
197
|
+
.map((name) => {
|
|
198
|
+
// Convert hyphenated name to camelCase then append Formatter
|
|
199
|
+
// e.g. "php-cs-fixer" → "phpCsFixerFormatter", "clang-format" → "clangFormatFormatter"
|
|
200
|
+
const camel = name.replace(/-([a-z])/g, (_, c: string) =>
|
|
201
|
+
c.toUpperCase(),
|
|
202
|
+
);
|
|
203
|
+
const key = `${camel}Formatter` as keyof typeof formatters;
|
|
204
|
+
return formatters[key] as FormatterInfo;
|
|
205
|
+
})
|
|
206
|
+
.filter(Boolean);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Assert file hasn't changed before editing
|
|
211
|
+
* Throws FileTimeError if file modified externally
|
|
212
|
+
*/
|
|
213
|
+
assertUnchanged(filePath: string): void {
|
|
214
|
+
this.fileTime.assert(filePath);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Check if file has changed externally
|
|
219
|
+
*/
|
|
220
|
+
hasChanged(filePath: string): boolean {
|
|
221
|
+
return this.fileTime.hasChanged(filePath);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Record file read (after agent reads file)
|
|
226
|
+
*/
|
|
227
|
+
recordRead(filePath: string): void {
|
|
228
|
+
this.fileTime.read(filePath);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Clear detection cache
|
|
233
|
+
*/
|
|
234
|
+
clearCache(): void {
|
|
235
|
+
clearFormatterRuntimeState();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --- Singleton Instance ---
|
|
240
|
+
|
|
241
|
+
let globalFormatService: FormatService | null = null;
|
|
242
|
+
let currentSessionID: string | null = null;
|
|
243
|
+
|
|
244
|
+
export function getFormatService(
|
|
245
|
+
sessionID?: string,
|
|
246
|
+
enabled: boolean = true,
|
|
247
|
+
): FormatService {
|
|
248
|
+
// Create new instance if:
|
|
249
|
+
// 1. No service exists yet
|
|
250
|
+
// 2. Session ID changed (different session)
|
|
251
|
+
const shouldCreateNew =
|
|
252
|
+
!globalFormatService || (sessionID && sessionID !== currentSessionID);
|
|
253
|
+
|
|
254
|
+
if (shouldCreateNew) {
|
|
255
|
+
globalFormatService = new FormatService(sessionID ?? "default", enabled);
|
|
256
|
+
currentSessionID = sessionID ?? "default";
|
|
257
|
+
}
|
|
258
|
+
return globalFormatService!;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function resetFormatService(): void {
|
|
262
|
+
clearFormatterRuntimeState();
|
|
263
|
+
globalFormatService = null;
|
|
264
|
+
currentSessionID = null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Reset format service and clear all file tracking state.
|
|
269
|
+
* Use this in tests to ensure complete isolation.
|
|
270
|
+
*/
|
|
271
|
+
export function clearFormatServiceAndFileState(): void {
|
|
272
|
+
resetFormatService();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Re-export for convenience
|
|
276
|
+
export { clearAllSessions } from "./file-time.js";
|