pipcode 0.0.0-stage → 2.1.1
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/LICENSE +47 -0
- package/README.md +44 -2
- package/auto.js +40 -0
- package/bin/setup.js +31 -0
- package/bin/teardown.js +18 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/index.js +39 -0
- package/package.json +45 -4
- package/src/auth/auth-email.js +32 -0
- package/src/auth/trusted-key.js +13 -0
- package/src/auto/adjust.js +41 -0
- package/src/auto/amnesia.js +37 -0
- package/src/auto/anonymize-tasks.js +13 -0
- package/src/auto/apply-diff-backed.js +10 -0
- package/src/auto/apply-diff.js +60 -0
- package/src/auto/ask-guard.js +16 -0
- package/src/auto/ask-pause.js +36 -0
- package/src/auto/ask-protocol.js +6 -0
- package/src/auto/ask-user.js +29 -0
- package/src/auto/backup-touched.js +30 -0
- package/src/auto/budget.js +52 -0
- package/src/auto/cancel.js +20 -0
- package/src/auto/constants.js +30 -0
- package/src/auto/crypto-util.js +17 -0
- package/src/auto/decompose.js +47 -0
- package/src/auto/diff.js +19 -0
- package/src/auto/fs-list.js +34 -0
- package/src/auto/git-base.js +16 -0
- package/src/auto/git-init.js +39 -0
- package/src/auto/git-restore.js +34 -0
- package/src/auto/git-scope.js +10 -0
- package/src/auto/git.js +38 -0
- package/src/auto/mission-docs.js +29 -0
- package/src/auto/mission-line.js +22 -0
- package/src/auto/node-check.js +30 -0
- package/src/auto/observe.js +23 -0
- package/src/auto/orchestrator.js +30 -0
- package/src/auto/patch-ask.js +16 -0
- package/src/auto/paths.js +18 -0
- package/src/auto/plan.js +42 -0
- package/src/auto/pricing.js +49 -0
- package/src/auto/project-files.js +12 -0
- package/src/auto/prompts.js +24 -0
- package/src/auto/quote-format.js +33 -0
- package/src/auto/retry.js +40 -0
- package/src/auto/run-lock.js +40 -0
- package/src/auto/run-loop.js +38 -0
- package/src/auto/run.js +49 -0
- package/src/auto/snapshot-gate.js +51 -0
- package/src/auto/snapshot-paths.js +33 -0
- package/src/auto/snapshot-restore.js +59 -0
- package/src/auto/snapshot-take.js +30 -0
- package/src/auto/state.js +52 -0
- package/src/auto/step-commit.js +46 -0
- package/src/auto/step-green.js +38 -0
- package/src/auto/step-loop.js +56 -0
- package/src/auto/step-prep.js +60 -0
- package/src/auto/step-qa.js +53 -0
- package/src/auto/step.js +30 -0
- package/src/auto/stop-options.js +34 -0
- package/src/auto/stop-report.js +17 -0
- package/src/auto/tasks.js +44 -0
- package/src/cli/apply-loop.js +29 -0
- package/src/cli/apply-toml.js +26 -0
- package/src/cli/apply.js +45 -0
- package/src/cli/ask-prepare.js +37 -0
- package/src/cli/auth-branches.js +53 -0
- package/src/cli/auth-interactive.js +46 -0
- package/src/cli/auto-ask.js +33 -0
- package/src/cli/auto-audit.js +16 -0
- package/src/cli/auto-cli.js +46 -0
- package/src/cli/auto-consent.js +22 -0
- package/src/cli/auto-flags.js +39 -0
- package/src/cli/auto-gate-session.js +27 -0
- package/src/cli/auto-gate.js +34 -0
- package/src/cli/auto-loader.js +17 -0
- package/src/cli/auto-plan.js +36 -0
- package/src/cli/auto-preflight.js +35 -0
- package/src/cli/auto-quote.js +28 -0
- package/src/cli/auto-report.js +36 -0
- package/src/cli/auto-resume.js +28 -0
- package/src/cli/auto-rules.js +20 -0
- package/src/cli/auto-run.js +26 -0
- package/src/cli/auto-task.js +26 -0
- package/src/cli/banner.js +20 -0
- package/src/cli/centralize.js +23 -0
- package/src/cli/collect-step.js +25 -0
- package/src/cli/config-pairs.js +46 -0
- package/src/cli/config.js +51 -0
- package/src/cli/detect-os.js +11 -0
- package/src/cli/dispatch.js +36 -0
- package/src/cli/env-file.js +44 -0
- package/src/cli/env-load.js +28 -0
- package/src/cli/env-parse.js +39 -0
- package/src/cli/env-rebuild.js +33 -0
- package/src/cli/gitignore-env.js +46 -0
- package/src/cli/help.js +28 -0
- package/src/cli/ide-entries.js +42 -0
- package/src/cli/ide-paths.js +37 -0
- package/src/cli/ide-roots.js +25 -0
- package/src/cli/interactive.js +26 -0
- package/src/cli/jetbrains.js +40 -0
- package/src/cli/legacy-entry.js +17 -0
- package/src/cli/main.js +42 -0
- package/src/cli/memory-cli.js +15 -0
- package/src/cli/prompts.js +24 -0
- package/src/cli/server-entry.js +42 -0
- package/src/cli/setup-args.js +39 -0
- package/src/cli/target-manual.js +24 -0
- package/src/cli/target-select.js +51 -0
- package/src/cli/toml-merge.js +56 -0
- package/src/cli/trial-check.js +15 -0
- package/src/cli/welcome.js +7 -0
- package/src/core/abort-signal.js +38 -0
- package/src/core/auto-deps.js +29 -0
- package/src/core/cache-ttl.js +21 -0
- package/src/core/config-base.js +38 -0
- package/src/core/config-cache.js +50 -0
- package/src/core/config.js +58 -0
- package/src/core/constants.js +31 -0
- package/src/core/crypto.js +45 -0
- package/src/core/dlp.js +51 -0
- package/src/core/doctrine-all.js +31 -0
- package/src/core/doctrine-caches.js +10 -0
- package/src/core/doctrine-fetch.js +49 -0
- package/src/core/doctrine-one.js +18 -0
- package/src/core/doctrine.js +39 -0
- package/src/core/dual-stream-parser.js +55 -0
- package/src/core/exports.js +28 -0
- package/src/core/google-route.js +17 -0
- package/src/core/identity.js +23 -0
- package/src/core/lang.js +19 -0
- package/src/core/leak-guard.js +40 -0
- package/src/core/llm-builder-anthropic.js +26 -0
- package/src/core/llm-builder-gemini.js +22 -0
- package/src/core/llm-builder-openai.js +26 -0
- package/src/core/llm-call.js +53 -0
- package/src/core/llm-config-secondary.js +28 -0
- package/src/core/llm-config.js +32 -0
- package/src/core/llm-errors.js +25 -0
- package/src/core/llm-exchange.js +27 -0
- package/src/core/llm-extract.js +52 -0
- package/src/core/llm-label.js +33 -0
- package/src/core/llm-ollama-request.js +32 -0
- package/src/core/llm-ollama.js +42 -0
- package/src/core/llm-prompts.js +14 -0
- package/src/core/llm-providers.js +26 -0
- package/src/core/llm-request.js +17 -0
- package/src/core/llm-retry-429.js +23 -0
- package/src/core/llm-stream-events.js +29 -0
- package/src/core/llm-stream.js +42 -0
- package/src/core/llm-unbounded.js +15 -0
- package/src/core/llm-warmup.js +44 -0
- package/src/core/manifest.js +42 -0
- package/src/core/master-prompt.js +11 -0
- package/src/core/model-id.js +19 -0
- package/src/core/ollama-lines.js +48 -0
- package/src/core/ollama.js +43 -0
- package/src/core/pkg-manager.js +35 -0
- package/src/core/process-guards.js +47 -0
- package/src/core/progress-emit.js +25 -0
- package/src/core/purge-key.js +20 -0
- package/src/core/reasoning-cut.js +19 -0
- package/src/core/reasoning-models.js +8 -0
- package/src/core/session-net.js +60 -0
- package/src/core/session-resilient.js +44 -0
- package/src/core/session.js +49 -0
- package/src/core/sse-parser.js +25 -0
- package/src/core/stdout-guard.js +11 -0
- package/src/core/tier.js +48 -0
- package/src/core/trial-resolve.js +22 -0
- package/src/core/trial-session.js +60 -0
- package/src/core/trial-status.js +35 -0
- package/src/core/usage-cost.js +25 -0
- package/src/core/usage-extract.js +28 -0
- package/src/core/usage-meter.js +41 -0
- package/src/engine/analyze-need.js +24 -0
- package/src/engine/analyze-ux.js +42 -0
- package/src/engine/ask-history.js +43 -0
- package/src/engine/ask-llm.js +40 -0
- package/src/engine/ask-prompt.js +48 -0
- package/src/engine/ask-rules.js +13 -0
- package/src/engine/ast-call-rules-danger.js +24 -0
- package/src/engine/ast-call-rules-inject.js +29 -0
- package/src/engine/ast-context.js +28 -0
- package/src/engine/ast-import-helpers.js +46 -0
- package/src/engine/ast-lang.js +24 -0
- package/src/engine/ast-node-helpers.js +51 -0
- package/src/engine/ast-rules-calls.js +16 -0
- package/src/engine/ast-rules-graph.js +58 -0
- package/src/engine/ast-rules-js.js +30 -0
- package/src/engine/ast-rules-jsx-py.js +26 -0
- package/src/engine/ast-runtime.js +60 -0
- package/src/engine/ast-scan.js +48 -0
- package/src/engine/audit-citations.js +28 -0
- package/src/engine/audit-facts.js +47 -0
- package/src/engine/audit-report.js +40 -0
- package/src/engine/audit-task.js +37 -0
- package/src/engine/cap-cache.js +50 -0
- package/src/engine/cap-eval.js +49 -0
- package/src/engine/cap-http.js +32 -0
- package/src/engine/cap-meta-anthropic.js +15 -0
- package/src/engine/cap-meta-ollama.js +15 -0
- package/src/engine/cap-meta-openrouter.js +19 -0
- package/src/engine/cap-meta.js +19 -0
- package/src/engine/cap-probe.js +34 -0
- package/src/engine/cap-types.js +29 -0
- package/src/engine/chat-doctrine.js +31 -0
- package/src/engine/checkpoint-control.js +23 -0
- package/src/engine/checkpoint-detect.js +19 -0
- package/src/engine/checkpoint-rules.js +19 -0
- package/src/engine/code-decls.js +12 -0
- package/src/engine/code-graph.js +52 -0
- package/src/engine/coerce.js +8 -0
- package/src/engine/collect.js +21 -0
- package/src/engine/compute-impact.js +17 -0
- package/src/engine/context-files.js +42 -0
- package/src/engine/context-fit.js +28 -0
- package/src/engine/context-margin.js +37 -0
- package/src/engine/context-relay.js +60 -0
- package/src/engine/control-duo.js +38 -0
- package/src/engine/control-phase.js +32 -0
- package/src/engine/control-prompt.js +25 -0
- package/src/engine/control-verdict.js +36 -0
- package/src/engine/default-steps.js +7 -0
- package/src/engine/deps-parse-js.js +49 -0
- package/src/engine/deps-parse-more.js +30 -0
- package/src/engine/deps-parse-other.js +41 -0
- package/src/engine/deps-parse.js +31 -0
- package/src/engine/det-builders-1.js +38 -0
- package/src/engine/det-builders-2.js +50 -0
- package/src/engine/detect-domains.js +27 -0
- package/src/engine/diff-files.js +35 -0
- package/src/engine/doc-suite.js +39 -0
- package/src/engine/doctrine-prefix.js +13 -0
- package/src/engine/domain-regex.js +11 -0
- package/src/engine/exports.js +35 -0
- package/src/engine/extract-deterministic.js +33 -0
- package/src/engine/extract-llm.js +48 -0
- package/src/engine/facts-binary.js +12 -0
- package/src/engine/facts-embedded.js +18 -0
- package/src/engine/facts-functions.js +21 -0
- package/src/engine/facts-html.js +22 -0
- package/src/engine/facts-inventory.js +33 -0
- package/src/engine/facts-lang.js +34 -0
- package/src/engine/facts-measure.js +43 -0
- package/src/engine/facts-patterns.js +28 -0
- package/src/engine/facts-project.js +26 -0
- package/src/engine/facts-size.js +26 -0
- package/src/engine/facts-wordpress.js +36 -0
- package/src/engine/generate-architecture-guide.js +46 -0
- package/src/engine/generate-readme.js +38 -0
- package/src/engine/generate-runbook.js +47 -0
- package/src/engine/git-facts.js +16 -0
- package/src/engine/history-purge.js +16 -0
- package/src/engine/json-balance.js +37 -0
- package/src/engine/json-safe.js +13 -0
- package/src/engine/line-numbers.js +16 -0
- package/src/engine/md-build-mid.js +35 -0
- package/src/engine/md-build-top.js +58 -0
- package/src/engine/md-build.js +36 -0
- package/src/engine/md-parse-helpers.js +7 -0
- package/src/engine/md-parse-pillars.js +38 -0
- package/src/engine/md-parse-ux-design.js +31 -0
- package/src/engine/md-parse.js +33 -0
- package/src/engine/mission-context-prefix.js +30 -0
- package/src/engine/mission-guard.js +36 -0
- package/src/engine/mission-helpers.js +23 -0
- package/src/engine/mission-log.js +56 -0
- package/src/engine/mission-paths.js +7 -0
- package/src/engine/mission-store.js +51 -0
- package/src/engine/normalize-extraction.js +35 -0
- package/src/engine/normalize-impact-design.js +39 -0
- package/src/engine/normalize-pillars.js +36 -0
- package/src/engine/orchestrate-route.js +39 -0
- package/src/engine/orchestrator-build.js +37 -0
- package/src/engine/orchestrator-phases.js +55 -0
- package/src/engine/orchestrator.js +40 -0
- package/src/engine/osv.js +43 -0
- package/src/engine/parse-json-loose.js +36 -0
- package/src/engine/patch-file-sections.js +28 -0
- package/src/engine/patch-scan-cheat.js +52 -0
- package/src/engine/patch-scan-prep.js +37 -0
- package/src/engine/patch-scan-rules.js +51 -0
- package/src/engine/patch-scan.js +38 -0
- package/src/engine/patch-secret-guard.js +50 -0
- package/src/engine/patch-ui-guard.js +49 -0
- package/src/engine/pattern-sanitize.js +23 -0
- package/src/engine/plan-draft.js +37 -0
- package/src/engine/plan-mission.js +60 -0
- package/src/engine/plan-reply-legacy.js +34 -0
- package/src/engine/plan-reply-pillars.js +34 -0
- package/src/engine/plan-reply-ux.js +17 -0
- package/src/engine/plan-reply.js +28 -0
- package/src/engine/plan-summary.js +16 -0
- package/src/engine/preflight-text.js +27 -0
- package/src/engine/preflight.js +27 -0
- package/src/engine/project-brief.js +41 -0
- package/src/engine/project-context.js +49 -0
- package/src/engine/project-instructions.js +19 -0
- package/src/engine/py-imports.js +31 -0
- package/src/engine/relay-analysis.js +44 -0
- package/src/engine/relay-digest.js +42 -0
- package/src/engine/route-llm.js +25 -0
- package/src/engine/rule-text.js +12 -0
- package/src/engine/self-assessment.js +33 -0
- package/src/engine/steps-block.js +19 -0
- package/src/engine/ux-violations-leak.js +43 -0
- package/src/engine/vuln-facts.js +37 -0
- package/src/fs/atomic-text.js +45 -0
- package/src/fs/atomic-write.js +21 -0
- package/src/fs/backup-once.js +27 -0
- package/src/fs/config-path.js +39 -0
- package/src/fs/config-read.js +23 -0
- package/src/fs/config-write.js +60 -0
- package/src/fs/home-root.js +55 -0
- package/src/fs/load-config.js +36 -0
- package/src/fs/os-errors.js +26 -0
- package/src/fs/permissions.js +48 -0
- package/src/mcp/boot-warmup.js +9 -0
- package/src/mcp/call-tool.js +20 -0
- package/src/mcp/client-info.js +35 -0
- package/src/mcp/engine-rules.js +16 -0
- package/src/mcp/handle-dlp.js +37 -0
- package/src/mcp/handle-licence.js +46 -0
- package/src/mcp/handle-list.js +15 -0
- package/src/mcp/handle-memory.js +37 -0
- package/src/mcp/handle-skill-fetch.js +50 -0
- package/src/mcp/handle-skill.js +38 -0
- package/src/mcp/licence-text.js +29 -0
- package/src/mcp/prompt-licence.js +11 -0
- package/src/mcp/server-create.js +33 -0
- package/src/mcp/server-main.js +39 -0
- package/src/mcp/tool-dispatch.js +28 -0
- package/src/mcp/tool-dlp.js +20 -0
- package/src/mcp/tool-licence.js +17 -0
- package/src/mcp/tool-memory.js +32 -0
- package/src/mcp/tool-skill.js +24 -0
- package/src/mcp/tools-list.js +13 -0
- package/src/memory/exports.js +5 -0
- package/src/memory/memory-filter.js +32 -0
- package/src/memory/memory-gate.js +21 -0
- package/src/memory/memory-store.js +44 -0
- package/src/net/auth-browser.js +49 -0
- package/src/net/auth-endpoints.js +33 -0
- package/src/net/local-fetch.js +31 -0
- package/src/net/loopback.js +8 -0
- package/src/net/proxy-env.js +59 -0
- package/src/project-memory/batches.js +17 -0
- package/src/project-memory/cahiers.js +42 -0
- package/src/project-memory/decision-settle.js +15 -0
- package/src/project-memory/decisions.js +36 -0
- package/src/project-memory/index-build.js +35 -0
- package/src/project-memory/memory-build.js +16 -0
- package/src/project-memory/memory-tags.js +40 -0
- package/src/project-memory/pending.js +24 -0
- package/src/project-memory/qa-apply.js +39 -0
- package/src/project-memory/qa-tags.js +28 -0
- package/src/project-memory/recall.js +38 -0
- package/src/project-memory/store.js +37 -0
- package/src/project-memory/window.js +13 -0
- package/src/rules/rules-advisor.js +17 -0
- package/src/rules/rules-frugality.js +17 -0
- package/src/rules/rules-load.js +56 -0
- package/src/rules/rules-project.js +16 -0
- package/src/rules/rules-registry.js +32 -0
- package/src/rules/rules-validate-pricing.js +22 -0
- package/src/rules/rules-validate-scan.js +45 -0
- package/src/rules/rules-validate.js +53 -0
- package/src/teardown/consent.js +31 -0
- package/src/teardown/flags.js +39 -0
- package/src/teardown/main.js +41 -0
- package/src/teardown/plan.js +31 -0
- package/src/teardown/remove-entry.js +39 -0
- package/src/teardown/remove-file.js +37 -0
- package/src/teardown/remove-toml.js +33 -0
- package/src/teardown/report.js +34 -0
- package/src/types/ast.d.ts +40 -0
- package/src/types/auto-ask.d.ts +6 -0
- package/src/types/auto-orch.d.ts +59 -0
- package/src/types/auto.d.ts +45 -0
- package/src/types/capability.d.ts +12 -0
- package/src/types/cli-auto-gate.d.ts +45 -0
- package/src/types/cli-auto.d.ts +42 -0
- package/src/types/config.d.ts +53 -0
- package/src/types/control.d.ts +56 -0
- package/src/types/docs.d.ts +6 -0
- package/src/types/doctrine.d.ts +22 -0
- package/src/types/llm.d.ts +28 -0
- package/src/types/mcp.d.ts +32 -0
- package/src/types/memory.d.ts +17 -0
- package/src/types/mission.d.ts +46 -0
- package/src/types/orchestrator.d.ts +33 -0
- package/src/types/routing.d.ts +25 -0
- package/src/types/rules.d.ts +30 -0
- package/src/types/snapshots.d.ts +25 -0
- package/src/types/teardown.d.ts +13 -0
- package/src/types/usage.d.ts +30 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/engine/domain-regex.js — Helpers regex pour la détection de domaines métier.
|
|
2
|
+
// Caractères « mot » : alphanumérique + accents FR (bornes lookahead/lookbehind
|
|
3
|
+
// pour éviter les faux positifs de sous-chaîne : « ai » dans « utilitaire »).
|
|
4
|
+
const WORD_CHARS = "a-z0-9àâäéèêëïîôöùûüçñ";
|
|
5
|
+
/** @param {string} s @returns {string} */
|
|
6
|
+
export const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7
|
+
/** @param {string} kw @returns {RegExp} */
|
|
8
|
+
export function keywordRegex(kw) {
|
|
9
|
+
return new RegExp(`(?<![${WORD_CHARS}])${escapeRe(kw)}(?![${WORD_CHARS}])`, "i");
|
|
10
|
+
}
|
|
11
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/engine/exports.js — Surface publique du domaine engine (Phase 0, Auto
|
|
2
|
+
// Mode, orchestration LLM, AST) : agrégation des re-exports consommés par les
|
|
3
|
+
// tests via le hub index.js.
|
|
4
|
+
export { orchestrateLLM } from "./orchestrator.js";
|
|
5
|
+
export { defaultSteps } from "./default-steps.js";
|
|
6
|
+
export { detectDomains, mapDomainsToSlugs } from "./detect-domains.js";
|
|
7
|
+
export { analyzeUxDesign } from "./analyze-ux.js";
|
|
8
|
+
export { analyzeNeed } from "./analyze-need.js";
|
|
9
|
+
export { computeImpact } from "./compute-impact.js";
|
|
10
|
+
export { riskLevel, deriveProjectName } from "./mission-helpers.js";
|
|
11
|
+
export { sanitizePattern } from "./pattern-sanitize.js";
|
|
12
|
+
export { extractMissionDeterministic } from "./extract-deterministic.js";
|
|
13
|
+
export { normalizeExtraction } from "./normalize-extraction.js";
|
|
14
|
+
export { parseJsonLoose } from "./parse-json-loose.js";
|
|
15
|
+
export { extractMissionLLM, extractMission } from "./extract-llm.js";
|
|
16
|
+
export { buildMissionMarkdown } from "./md-build.js";
|
|
17
|
+
export { parseMissionMarkdown } from "./md-parse.js";
|
|
18
|
+
export { scanPatchForViolations } from "./patch-scan.js";
|
|
19
|
+
export { checkMissionGuard } from "./mission-guard.js";
|
|
20
|
+
export { planMission } from "./plan-mission.js";
|
|
21
|
+
export { formatPlanReply } from "./plan-reply.js";
|
|
22
|
+
export { formatConsoleSummary } from "./plan-summary.js";
|
|
23
|
+
export { readMissionContext, writeMission } from "./mission-store.js";
|
|
24
|
+
export { logIteration } from "./mission-log.js";
|
|
25
|
+
export { missionContextPrefix } from "./mission-context-prefix.js";
|
|
26
|
+
export { loadAstRuntime, _resetAstRuntime } from "./ast-runtime.js";
|
|
27
|
+
export { ensureDataLeakGuard, DATA_LEAK_VIOLATIONS } from "./ux-violations-leak.js";
|
|
28
|
+
export { collectFrontendShellViolations, isFrontendFile } from "./patch-ui-guard.js";
|
|
29
|
+
export { collectSecretTextViolations } from "./patch-secret-guard.js";
|
|
30
|
+
export { collectGraphViolations } from "./ast-rules-graph.js";
|
|
31
|
+
export { collectCheatViolations } from "./patch-scan-cheat.js";
|
|
32
|
+
export { fileSections } from "./patch-file-sections.js";
|
|
33
|
+
export { getRules, setRules, resetRules } from "../rules/rules-registry.js";
|
|
34
|
+
export { ensureRules } from "../rules/rules-load.js";
|
|
35
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/engine/extract-deterministic.js — Extracteur déterministe (hors-ligne, aucun LLM).
|
|
2
|
+
import { analyzeNeed } from "./analyze-need.js";
|
|
3
|
+
import { analyzeUxDesign } from "./analyze-ux.js";
|
|
4
|
+
import { deriveProjectName } from "./mission-helpers.js";
|
|
5
|
+
import { buildPersonas, buildConstraints } from "./det-builders-1.js";
|
|
6
|
+
import { buildPitfalls, buildGlossary, buildImpact } from "./det-builders-2.js";
|
|
7
|
+
|
|
8
|
+
// Produit un MissionPlan complet depuis le catalogue BUSINESS_DOMAINS + DOMAIN_PATTERNS/GLOSSARY.
|
|
9
|
+
/** @param {string} needText @returns {MissionPlan} */
|
|
10
|
+
export function extractMissionDeterministic(needText) {
|
|
11
|
+
const need = String(needText || "").trim();
|
|
12
|
+
const matrix = analyzeNeed(need);
|
|
13
|
+
const keys = matrix.domainKeys;
|
|
14
|
+
const projectName = deriveProjectName(need);
|
|
15
|
+
const personas = buildPersonas(matrix.domains);
|
|
16
|
+
const constraints = buildConstraints(matrix.domains, keys);
|
|
17
|
+
const pitfalls = buildPitfalls(matrix.domains, keys);
|
|
18
|
+
const glossary = buildGlossary(keys);
|
|
19
|
+
const impact = buildImpact(keys, matrix);
|
|
20
|
+
const { uxDirectives, designSystem } = analyzeUxDesign(keys);
|
|
21
|
+
return {
|
|
22
|
+
projectName, need, domainKeys: keys,
|
|
23
|
+
personas, constraints, pitfalls, glossary, impact,
|
|
24
|
+
uxDirectives, designSystem,
|
|
25
|
+
fiches: matrix.fiches,
|
|
26
|
+
autoMode: false,
|
|
27
|
+
createdAt: new Date().toISOString(),
|
|
28
|
+
validatedAt: null,
|
|
29
|
+
iterations: 0,
|
|
30
|
+
checkedSteps: impact.steps.map(() => false),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/engine/extract-llm.js — Extracteurs LLM (BYOK) + unifié (LLM puis repli déterministe).
|
|
2
|
+
import { callLLM } from "../core/llm-call.js";
|
|
3
|
+
import { getRules } from "../rules/rules-registry.js";
|
|
4
|
+
import { withLanguage } from "../core/lang.js";
|
|
5
|
+
import { EXTRACTION_MAX_TOKENS } from "../core/constants.js";
|
|
6
|
+
import { parseJsonLoose } from "./parse-json-loose.js";
|
|
7
|
+
import { normalizeExtraction } from "./normalize-extraction.js";
|
|
8
|
+
import { extractMissionDeterministic } from "./extract-deterministic.js";
|
|
9
|
+
|
|
10
|
+
// Appelle le provider primaire, parse le JSON, normalise. Jette en cas d'échec.
|
|
11
|
+
/** @param {string} needText @param {{ primary: ProviderPrimary } | null} pcfg @returns {Promise<MissionPlan>} */
|
|
12
|
+
export async function extractMissionLLM(needText, pcfg) {
|
|
13
|
+
const primary = pcfg && pcfg.primary;
|
|
14
|
+
if (!primary) {
|
|
15
|
+
const e = /** @type {EnrichedError} */ (new Error("Aucun provider primaire configuré pour l'extraction LLM."));
|
|
16
|
+
e.code = "no_provider";
|
|
17
|
+
throw e;
|
|
18
|
+
}
|
|
19
|
+
const text = await callLLM({
|
|
20
|
+
provider: primary.provider, key: primary.key,
|
|
21
|
+
model: primary.model, base: primary.base,
|
|
22
|
+
systemPrompt: withLanguage(getRules().prompts.mission),
|
|
23
|
+
maxTokens: EXTRACTION_MAX_TOKENS,
|
|
24
|
+
userText: String(needText || "").trim(),
|
|
25
|
+
});
|
|
26
|
+
const raw = parseJsonLoose(text);
|
|
27
|
+
if (!raw) {
|
|
28
|
+
const e = /** @type {EnrichedError} */ (new Error("Le LLM n'a pas retourné un JSON exploitable."));
|
|
29
|
+
e.code = "bad_json";
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
return normalizeExtraction(raw, needText);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Extracteur unifié : LLM si clés BYOK, sinon déterministe. Échec LLM → repli silencieux.
|
|
36
|
+
/** @param {string} needText @param {{ primary: ProviderPrimary } | null} [pcfg] @returns {Promise<MissionPlan>} */
|
|
37
|
+
export async function extractMission(needText, pcfg) {
|
|
38
|
+
if (pcfg && pcfg.primary) {
|
|
39
|
+
try {
|
|
40
|
+
return await extractMissionLLM(needText, pcfg);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const e = /** @type {EnrichedError} */ (err);
|
|
43
|
+
console.error(`[virtual-cto] Extraction LLM Phase 0 échouée (${e.code || e.message}) — repli déterministe.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return extractMissionDeterministic(needText);
|
|
47
|
+
}
|
|
48
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/engine/facts-binary.js — Fichiers que PipCode ne peut PAS lire en texte (binaires) : signalés comme non analysés, jamais passés sous silence. Blueprints Unreal (.uasset/.umap) : logique visuelle impossible à auditer ; seul le C++ voisin l'est.
|
|
2
|
+
const KINDS = /** @type {Array<[RegExp, string]>} */ ([
|
|
3
|
+
[/\.(uasset|umap)$/i, "Blueprints et assets Unreal (.uasset/.umap) — logique visuelle, impossible à auditer en texte ; seul le C++ du projet est audité"],
|
|
4
|
+
[/\.(exe|dll|so|dylib|class|jar|apk|aab|ipa)$/i, "binaires compilés (.exe, .dll, .jar, .class…) — leur contenu n'est pas auditable ; fournir les sources"],
|
|
5
|
+
]);
|
|
6
|
+
|
|
7
|
+
/** @param {string[]} files @returns {string} ligne prête à injecter ("" s'il n'y a aucun binaire de ces familles) */
|
|
8
|
+
export function binaryFacts(files) {
|
|
9
|
+
const rows = KINDS.map(([re, label]) => ({ n: files.filter((f) => re.test(f)).length, label })).filter((r) => r.n > 0);
|
|
10
|
+
return rows.length ? `Non analysables (binaires, jamais lus) : ${rows.map((r) => `${r.n} ${r.label}`).join(" ; ")}` : "";
|
|
11
|
+
}
|
|
12
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/engine/facts-embedded.js — Scripts JavaScript écrits DANS une page (balises <script> d'un .html, .vue, .svelte) : leur texte et la ligne où ils commencent, pour mesurer leurs fonctions comme celles d'un fichier .js.
|
|
2
|
+
// Les scripts externes (src=…), les modèles et les données (JSON, gabarits) sont ignorés. Motifs seulement.
|
|
3
|
+
const SCRIPT = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
4
|
+
|
|
5
|
+
/** @param {string} attrs @returns {boolean} script exécutable en JavaScript (sans type, « module », « javascript ») */
|
|
6
|
+
const isJs = (attrs) => !/\bsrc\s*=/i.test(attrs) && (!/\btype\s*=/i.test(attrs) || /\btype\s*=\s*["']?(?:module|text\/javascript|application\/javascript)/i.test(attrs));
|
|
7
|
+
|
|
8
|
+
/** @param {string} text @returns {Array<{ text: string, start: number }>} start = numéro de la première ligne du script (1 = première ligne du fichier) */
|
|
9
|
+
export function embeddedScripts(text) {
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const m of text.matchAll(SCRIPT)) {
|
|
12
|
+
if (!isJs(m[1])) continue;
|
|
13
|
+
const before = text.slice(0, (m.index ?? 0) + m[0].indexOf(">") + 1);
|
|
14
|
+
out.push({ text: m[2], start: before.split("\n").length });
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/engine/facts-functions.js — Fonctions d'un fichier et leur longueur, pour tous les langages de facts-lang.js. Estimation par motifs, jamais d'exécution ; langage absent de la table = aucune mesure.
|
|
2
|
+
import { LANGS } from "./facts-lang.js";
|
|
3
|
+
import { braceLength, endLength, indentLength } from "./facts-measure.js";
|
|
4
|
+
|
|
5
|
+
/** @param {string} file @returns {boolean} la mesure des fonctions existe pour ce langage */
|
|
6
|
+
export const isMeasured = (file) => LANGS.some(([re]) => re.test(file));
|
|
7
|
+
|
|
8
|
+
/** @param {string} file @param {string} text @returns {Array<{ name: string, line: number, lines: number }>} */
|
|
9
|
+
export function functionLengths(file, text) {
|
|
10
|
+
const lang = LANGS.find(([re]) => re.test(file))?.[1];
|
|
11
|
+
if (!lang) return [];
|
|
12
|
+
const lines = text.split("\n");
|
|
13
|
+
const measure = lang.kind === "indent" ? indentLength : lang.kind === "end" ? endLength : braceLength;
|
|
14
|
+
const out = [];
|
|
15
|
+
for (let i = 0; i < lines.length; i++) {
|
|
16
|
+
const name = lang.find(lines[i]);
|
|
17
|
+
if (name) out.push({ name, line: i + 1, lines: measure(lines, i) });
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/engine/facts-html.js — Comptes objectifs sur les pages HTML (attributs manquants, médias, scripts tiers, liens non chiffrés). Un indice chiffré par motif, jamais un rendu : le modèle en tire les conséquences.
|
|
2
|
+
// Motifs seulement ; chaque compte vient d'une lecture de texte, jamais d'un rendu.
|
|
3
|
+
const PAGE = /\.html?$/i;
|
|
4
|
+
|
|
5
|
+
/** @param {Array<{ file: string, text: string }>} entries @returns {string} ligne prête à injecter ("" sans page HTML) */
|
|
6
|
+
export function htmlFacts(entries) {
|
|
7
|
+
const pages = entries.filter((e) => PAGE.test(e.file));
|
|
8
|
+
if (pages.length === 0) return "";
|
|
9
|
+
const names = (/** @type {(t: string) => boolean} */ bad) => pages.filter((p) => bad(p.text)).map((p) => p.file);
|
|
10
|
+
const count = (/** @type {RegExp} */ re) => pages.reduce((n, p) => n + (p.text.match(re) ?? []).length, 0);
|
|
11
|
+
const noLang = names((t) => /<html\b/i.test(t) && !/<html\b[^>]*\blang\s*=/i.test(t));
|
|
12
|
+
const noTitle = names((t) => /<html\b/i.test(t) && !/<title\b[^>]*>\s*\S/i.test(t));
|
|
13
|
+
const noViewport = names((t) => /<html\b/i.test(t) && !/<meta\b[^>]*name\s*=\s*["']viewport/i.test(t));
|
|
14
|
+
const extScripts = pages.flatMap((p) => [...p.text.matchAll(/<script\b[^>]*\bsrc\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi)]);
|
|
15
|
+
const noSri = extScripts.filter((m) => !/\bintegrity\s*=/i.test(m[0])).length;
|
|
16
|
+
const hosts = [...new Set(pages.flatMap((p) => [...p.text.matchAll(/<(?:link|script|img|iframe|source|video|audio)\b[^>]*\b(?:href|src)\s*=\s*["']https?:\/\/([^/"':?#]+)/gi)].map((m) => m[1].toLowerCase())))].sort();
|
|
17
|
+
const list = (/** @type {string[]} */ a) => (a.length ? `${a.length} (${a.slice(0, 4).join(", ")}${a.length > 4 ? ", …" : ""})` : "aucune");
|
|
18
|
+
return [`Pages HTML : ${pages.length} — sans attribut de langue : ${list(noLang)} ; sans titre : ${list(noTitle)} ; sans adaptation mobile (viewport) : ${list(noViewport)}`,
|
|
19
|
+
`- Images sans texte alternatif : ${count(/<img\b(?![^>]*\balt\s*=)[^>]*>/gi)} ; gestionnaires dans le balisage (onclick…) : ${count(/\son(?:click|load|error|submit|change|mouse\w+)\s*=/gi)} ; scripts externes : ${extScripts.length} dont ${noSri} sans signature d'intégrité ; ressources en clair http:// : ${count(/(?:src|href)\s*=\s*["']http:\/\//gi)}`,
|
|
20
|
+
`- Ressources de tiers chargées par la page (hôtes ; données visiteur transmises à ces tiers) : ${hosts.length ? hosts.join(", ") : "aucune"}`].join("\n");
|
|
21
|
+
}
|
|
22
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/engine/facts-inventory.js — Inventaire COMPLET des fichiers du projet (toutes extensions : html, css, txt, md, tout langage), pour qu'un audit ne déclare jamais « absent » un fichier qui existe.
|
|
2
|
+
// Noms seulement : le contenu des fichiers sensibles (.env, clés) n'est jamais lu. Dossiers lourds exclus ; borné en profondeur et en nombre.
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { SKIP_DIRS, isSecretPath } from "../auto/fs-list.js";
|
|
6
|
+
|
|
7
|
+
export const MAX_FILES = 600;
|
|
8
|
+
const MAX_DEPTH = 6;
|
|
9
|
+
|
|
10
|
+
/** @param {string} root @param {string} rel @param {number} depth @param {string[]} out @returns {Promise<void>} */
|
|
11
|
+
async function walk(root, rel, depth, out) {
|
|
12
|
+
const entries = await fs.readdir(path.join(root, rel), { withFileTypes: true }).catch(() => []);
|
|
13
|
+
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
14
|
+
if (out.length >= MAX_FILES) return;
|
|
15
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
16
|
+
if (e.isDirectory() && depth < MAX_DEPTH && !SKIP_DIRS.has(e.name)) await walk(root, r, depth + 1, out);
|
|
17
|
+
else if (e.isFile()) out.push(r);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @param {string} root @returns {Promise<string[]>} chemins relatifs « / » de tous les fichiers (sensibles compris : à ne jamais lire) */
|
|
22
|
+
export async function walkFiles(root) {
|
|
23
|
+
/** @type {string[]} */ const out = [];
|
|
24
|
+
await walk(root, "", 0, out);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @param {string[]} files @returns {string} ligne d'inventaire prête à injecter */
|
|
29
|
+
export function inventoryFacts(files) {
|
|
30
|
+
const list = files.map((f) => (isSecretPath(f) ? `${f} (sensible, contenu jamais lu)` : f));
|
|
31
|
+
return `Inventaire complet des fichiers (${files.length}${files.length >= MAX_FILES ? ", limite atteinte" : ""}) — un fichier listé ici EXISTE, ne le déclare jamais absent : ${list.join(", ") || "(dossier vide)"}`;
|
|
32
|
+
}
|
|
33
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/engine/facts-lang.js — Familles de langages mesurées (début de fonction, mode de fin) : accolades (JS/TS, Go, Rust, PHP, Java, C#, Kotlin, Scala, Swift, C/C++, shell, PowerShell), indentation (Python), mots-clés « end » (Ruby).
|
|
2
|
+
// Détection par motifs de ligne : estimation, jamais d'exécution. Un langage absent de cette table n'a pas de mesure de fonctions (jamais d'invention).
|
|
3
|
+
const CONTROL = new Set(["if", "else", "for", "while", "switch", "catch", "do", "try", "return", "new", "throw", "await", "yield", "case", "delete", "typeof", "using", "lock", "foreach", "synchronized", "elif", "when", "defer", "go", "sizeof", "assert", "print", "echo"]);
|
|
4
|
+
const JS = /^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function\*?\s+([A-Za-z_$][\w$]*)|(?:const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[\w$]+)\s*=>)/;
|
|
5
|
+
const MODS = "(?:(?:public|private|protected|internal|static|final|abstract|override|open|suspend|inline|operator|async|virtual|unsafe|extern|const|pub(?:\\([^)]*\\))?|mutating|class|implicit|fileprivate|synchronized)\\s+)*";
|
|
6
|
+
const word = (/** @type {string} */ kw, /** @type {string} */ nameRe = "(\\w+)") => new RegExp(`^\\s*${MODS}${kw}\\s+${nameRe}`);
|
|
7
|
+
|
|
8
|
+
/** Java / C# / C / C++ : « type nom(…) » sans « ; » final, jamais un mot de contrôle. @param {string} line @returns {string | undefined} */
|
|
9
|
+
function clike(line) {
|
|
10
|
+
const t = line.trimEnd();
|
|
11
|
+
if (t.endsWith(";") || t.trimStart().startsWith("//")) return undefined;
|
|
12
|
+
const m = /^\s*(?:[A-Za-z_][\w<>[\],.?*&:]*\s+)+?([A-Za-z_]\w*)\s*\(/.exec(t);
|
|
13
|
+
const first = /^\s*([A-Za-z_]\w*)/.exec(t)?.[1] ?? "";
|
|
14
|
+
return m && !CONTROL.has(first) && !CONTROL.has(m[1]) ? m[1] : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** @typedef {{ kind: "brace" | "indent" | "end", find: (line: string) => string | undefined }} Lang */
|
|
18
|
+
/** @type {Array<[RegExp, Lang]>} */
|
|
19
|
+
export const LANGS = [
|
|
20
|
+
[/\.(js|mjs|cjs|ts|mts|cts|tsx|jsx)$/i, { kind: "brace", find: (l) => { const m = JS.exec(l); return m ? m[1] || m[2] : undefined; } }],
|
|
21
|
+
[/\.go$/i, { kind: "brace", find: (l) => /^func\s+(?:\([^)]*\)\s*)?(\w+)\s*[([]/.exec(l)?.[1] }],
|
|
22
|
+
[/\.rs$/i, { kind: "brace", find: (l) => word("fn").exec(l)?.[1] }],
|
|
23
|
+
[/\.php$/i, { kind: "brace", find: (l) => word("function", "&?(\\w+)\\s*\\(").exec(l)?.[1] }],
|
|
24
|
+
[/\.swift$/i, { kind: "brace", find: (l) => word("func").exec(l)?.[1] }],
|
|
25
|
+
[/\.(kt|kts)$/i, { kind: "brace", find: (l) => word("fun", "(?:<[^>]+>\\s*)?(?:[\\w.]+\\.)?(\\w+)").exec(l)?.[1] }],
|
|
26
|
+
[/\.scala$/i, { kind: "brace", find: (l) => word("def").exec(l)?.[1] }],
|
|
27
|
+
[/\.(java|cs|c|cpp|cc|h|hpp|m|mm|dart|groovy)$/i, { kind: "brace", find: clike }],
|
|
28
|
+
[/\.(sh|bash)$/i, { kind: "brace", find: (l) => /^\s*(?:function\s+)?([\w-]+)\s*\(\)\s*\{?\s*$|^\s*function\s+([\w-]+)/.exec(l)?.slice(1).find(Boolean) }],
|
|
29
|
+
[/\.ps1$/i, { kind: "brace", find: (l) => /^\s*function\s+([\w-]+)/i.exec(l)?.[1] }],
|
|
30
|
+
[/\.py$/i, { kind: "indent", find: (l) => /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/.exec(l)?.[1] }],
|
|
31
|
+
[/\.gd$/i, { kind: "indent", find: (l) => /^\s*(?:static\s+)?func\s+([A-Za-z_]\w*)/.exec(l)?.[1] }], // GDScript (Godot) : indentation comme Python
|
|
32
|
+
[/\.rb$/i, { kind: "end", find: (l) => /^\s*def\s+(?:self\.)?([\w?!=]+)/.exec(l)?.[1] }],
|
|
33
|
+
];
|
|
34
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/engine/facts-measure.js — Mesure de la longueur d'une fonction (lignes UTILES : ni vides ni commentaire) selon la façon dont le langage la délimite : indentation, accolades, mots-clés « end ». Estimation par motifs.
|
|
2
|
+
const COMMENT = /^\s*(\/\/|#|\/\*|\*|--)/;
|
|
3
|
+
const OPEN = /^\s*(?:def|class|module|if|unless|while|until|case|begin|for)\b|\bdo(?:\s*\|[^|]*\|)?\s*$/;
|
|
4
|
+
|
|
5
|
+
/** @param {string} line @returns {boolean} */
|
|
6
|
+
const useful = (line) => line.trim() !== "" && !COMMENT.test(line);
|
|
7
|
+
const indentOf = (/** @type {string} */ l) => (/^\s*/.exec(l) ?? [""])[0].length;
|
|
8
|
+
|
|
9
|
+
/** @param {string[]} lines @param {number} from @returns {number} jusqu'à la première ligne moins indentée */
|
|
10
|
+
export function indentLength(lines, from) {
|
|
11
|
+
const base = indentOf(lines[from]); let n = 1;
|
|
12
|
+
for (let j = from + 1; j < lines.length; j++) {
|
|
13
|
+
if (lines[j].trim() === "") continue;
|
|
14
|
+
if (indentOf(lines[j]) <= base) break;
|
|
15
|
+
if (useful(lines[j])) n++;
|
|
16
|
+
}
|
|
17
|
+
return n;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** @param {string[]} lines @param {number} from @returns {number} accolades équilibrées ; instruction terminée sans accolade, ou aucune dans les 3 premières lignes = corps en expression ou déclaration (1 ligne) */
|
|
21
|
+
export function braceLength(lines, from) {
|
|
22
|
+
let depth = 0; let opened = false; let n = 0;
|
|
23
|
+
for (let j = from; j < lines.length; j++) {
|
|
24
|
+
if (useful(lines[j])) n++;
|
|
25
|
+
for (const c of lines[j]) { if (c === "{") { depth++; opened = true; } else if (c === "}") depth--; }
|
|
26
|
+
if (!opened && (lines[from].trimEnd().endsWith(";") || j >= from + 2)) return 1;
|
|
27
|
+
if (opened && depth <= 0) return n;
|
|
28
|
+
}
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** @param {string[]} lines @param {number} from @returns {number} Ruby : jusqu'au « end » qui équilibre les ouvertures */
|
|
33
|
+
export function endLength(lines, from) {
|
|
34
|
+
let depth = 0; let n = 0;
|
|
35
|
+
for (let j = from; j < lines.length; j++) {
|
|
36
|
+
if (useful(lines[j])) n++;
|
|
37
|
+
if (OPEN.test(lines[j])) depth++;
|
|
38
|
+
if (/^\s*end\b/.test(lines[j])) depth--;
|
|
39
|
+
if (depth <= 0 && j > from) return n;
|
|
40
|
+
}
|
|
41
|
+
return n;
|
|
42
|
+
}
|
|
43
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/engine/facts-patterns.js — Motifs de risque repérés ligne par ligne (secrets en dur MASQUÉS, appels dangereux, erreurs avalées, dette signalée). Motifs seulement : un indice, pas une preuve.
|
|
2
|
+
const RULES = /** @type {Array<[string, RegExp]>} */ ([
|
|
3
|
+
["secrets", /\b([A-Za-z_]*(?:password|passwd|secret|api_?key|token)[A-Za-z_]*)\s*[:=]\s*["']([^"'\s]{4,})["']/i],
|
|
4
|
+
["dangereux", /\b(eval\s*\(|new\s+Function\s*\(|os\.system\s*\(|shell\s*=\s*True|subprocess\.(?:call|run|Popen)\s*\(|child_process|innerHTML\s*=|dangerouslySetInnerHTML|pickle\.loads?\s*\()/],
|
|
5
|
+
["erreurs-avalees", /(catch\s*(?:\([^)]*\))?\s*\{\s*\}|except\s*:\s*(?:pass)?\s*$|except\s+\w+(?:\s+as\s+\w+)?\s*:\s*pass\b)/],
|
|
6
|
+
["dette", /\b(TODO|FIXME|HACK|XXX)\b/],
|
|
7
|
+
]);
|
|
8
|
+
const SHOWN = 8;
|
|
9
|
+
|
|
10
|
+
/** @param {string} v @returns {string} valeur masquée : jamais le secret en clair dans les faits */
|
|
11
|
+
const mask = (v) => `${v.slice(0, 2)}…`;
|
|
12
|
+
|
|
13
|
+
/** @param {Array<{ file: string, text: string }>} entries @returns {Record<string, { total: number, shown: string[] }>} */
|
|
14
|
+
export function patternFacts(entries) {
|
|
15
|
+
/** @type {Record<string, { total: number, shown: string[] }>} */ const out = Object.fromEntries(RULES.map(([k]) => [k, { total: 0, shown: [] }]));
|
|
16
|
+
for (const { file, text } of entries) {
|
|
17
|
+
text.split("\n").forEach((line, i) => {
|
|
18
|
+
for (const [kind, re] of RULES) {
|
|
19
|
+
const m = re.exec(line);
|
|
20
|
+
if (!m) continue;
|
|
21
|
+
out[kind].total++;
|
|
22
|
+
if (out[kind].shown.length < SHOWN) out[kind].shown.push(`${file}:${i + 1}` + (kind === "secrets" ? ` ${m[1]} = "${mask(m[2])}"` : kind === "dangereux" ? ` ${m[1].trim()}` : ""));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/engine/facts-project.js — Présence (ou absence) des éléments qui rendent un projet livrable : dépendances déclarées et verrouillées, tests, intégration continue, conteneur, configuration d'exemple, documentation.
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const MANIFESTS = ["package.json", "requirements.txt", "pyproject.toml", "Pipfile", "composer.json", "Gemfile", "go.mod", "Cargo.toml", "pom.xml", "build.gradle"];
|
|
6
|
+
const LOCKS = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "poetry.lock", "Pipfile.lock", "composer.lock", "Gemfile.lock", "go.sum", "Cargo.lock"];
|
|
7
|
+
const CI = [".github/workflows", ".gitlab-ci.yml", ".circleci", "azure-pipelines.yml", "Jenkinsfile"];
|
|
8
|
+
const TEST_FILE = /(^|[\\/])(test_[^\\/]+|[^\\/]+_test|[^\\/]+\.(?:test|spec))\.[a-z]+$|(^|[\\/])(tests?|__tests__)[\\/]/i;
|
|
9
|
+
|
|
10
|
+
/** @param {string} root @param {string[]} names @returns {Promise<string[]>} ceux qui existent */
|
|
11
|
+
async function present(root, names) {
|
|
12
|
+
const ok = await Promise.all(names.map((n) => fs.access(path.join(root, n)).then(() => true, () => false)));
|
|
13
|
+
return names.filter((_, i) => ok[i]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @param {string} root @param {string[]} files fichiers source déjà listés @returns {Promise<{ manifests: string[], locks: string[], ci: string[], docker: boolean, envExample: boolean, gitignore: "absent" | "sans-env" | "ok", readme: boolean, tests: number }>} */
|
|
17
|
+
export async function projectFacts(root, files) {
|
|
18
|
+
const [manifests, locks, ci, docker, envEx, readme, gi] = await Promise.all([
|
|
19
|
+
present(root, MANIFESTS), present(root, LOCKS), present(root, CI), present(root, ["Dockerfile", "docker-compose.yml", "compose.yaml"]),
|
|
20
|
+
present(root, [".env.example", ".env.sample", "env.example"]), present(root, ["README.md", "README", "readme.md", "README.rst"]),
|
|
21
|
+
fs.readFile(path.join(root, ".gitignore"), "utf8").catch(() => null),
|
|
22
|
+
]);
|
|
23
|
+
return { manifests, locks, ci, docker: docker.length > 0, envExample: envEx.length > 0, readme: readme.length > 0,
|
|
24
|
+
gitignore: gi === null ? "absent" : /^\s*\.env\b/m.test(gi) ? "ok" : "sans-env", tests: files.filter((f) => TEST_FILE.test(f)).length };
|
|
25
|
+
}
|
|
26
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/engine/facts-size.js — Tailles du code : fichiers (norme PipCode ≤ 60 lignes) et fonctions (≤ 30 lignes utiles), y compris les fonctions écrites dans les balises <script> des pages HTML, Vue et Svelte.
|
|
2
|
+
// Une page HTML n'est pas jugée sur sa longueur (c'est du balisage) : seules ses fonctions JavaScript le sont. Estimation par motifs.
|
|
3
|
+
import { functionLengths, isMeasured } from "./facts-functions.js";
|
|
4
|
+
import { embeddedScripts } from "./facts-embedded.js";
|
|
5
|
+
import { countLines } from "./line-numbers.js";
|
|
6
|
+
|
|
7
|
+
export const MAX_FILE_LINES = 60;
|
|
8
|
+
export const MAX_FN_LINES = 30;
|
|
9
|
+
export const CODE = /\.(js|mjs|cjs|ts|mts|cts|tsx|jsx|py|gd|go|rs|java|kt|kts|scala|groovy|rb|php|cs|c|cc|cpp|h|hpp|m|mm|swift|dart|lua|pl|r|ex|exs|hs|jl|vue|svelte|sh|bash|ps1)$/i;
|
|
10
|
+
const PAGE = /\.(html?|vue|svelte)$/i;
|
|
11
|
+
|
|
12
|
+
/** @param {string} file @param {string} text @returns {Array<{ name: string, line: number, lines: number }>} */
|
|
13
|
+
function fnsOf(file, text) {
|
|
14
|
+
const own = functionLengths(file, text);
|
|
15
|
+
return PAGE.test(file) ? [...own, ...embeddedScripts(text).flatMap((s) => functionLengths("inline.js", s.text).map((f) => ({ ...f, line: f.line + s.start - 1 })))] : own;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** @param {Array<{ file: string, text: string }>} entries @returns {{ code: number, total: number, bigFiles: Array<{ file: string, lines: number }>, bigFns: Array<{ name: string, file: string, line: number, lines: number }>, unmeasured: string[] }} */
|
|
19
|
+
export function sizeFacts(entries) {
|
|
20
|
+
const code = entries.filter((e) => CODE.test(e.file));
|
|
21
|
+
const sized = code.map((e) => ({ file: e.file, lines: countLines(e.text) })).sort((a, b) => b.lines - a.lines);
|
|
22
|
+
const bigFns = entries.filter((e) => CODE.test(e.file) || PAGE.test(e.file)).flatMap((e) => fnsOf(e.file, e.text).filter((f) => f.lines > MAX_FN_LINES).map((f) => ({ ...f, file: e.file }))).sort((a, b) => b.lines - a.lines);
|
|
23
|
+
const unmeasured = [...new Set(code.filter((e) => !isMeasured(e.file) && !PAGE.test(e.file)).map((e) => e.file.slice(e.file.lastIndexOf(".")).toLowerCase()))];
|
|
24
|
+
return { code: code.length, total: sized.reduce((n, s) => n + s.lines, 0), bigFiles: sized.filter((s) => s.lines > MAX_FILE_LINES), bigFns, unmeasured };
|
|
25
|
+
}
|
|
26
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/engine/facts-wordpress.js — Projets WordPress (thèmes, extensions, blocs Gutenberg, widgets Elementor) : détection et règles de sécurité propres à WordPress. Le contenu des pages vit en base de données : seuls les FICHIERS sont audités.
|
|
2
|
+
// Motifs seulement : un indice à confirmer en lisant le code, jamais une preuve.
|
|
3
|
+
import { jsonObject } from "./json-safe.js";
|
|
4
|
+
|
|
5
|
+
const HEADER = /(?:Theme Name|Plugin Name)\s*:/;
|
|
6
|
+
const INPUT = /\$_(?:GET|POST|REQUEST|COOKIE)\b/;
|
|
7
|
+
const CLEAN = /sanitize_|absint|intval|esc_|wp_unslash|\(int\)|isset\(|empty\(|wp_verify_nonce/;
|
|
8
|
+
const SQL = /\$wpdb->(?:query|get_results|get_var|get_row|get_col)\s*\(\s*["'][^)]*\$/;
|
|
9
|
+
|
|
10
|
+
/** @param {string[]} a @returns {string} */
|
|
11
|
+
const list = (a) => (a.length ? `${a.length} (${a.slice(0, 4).join(", ")}${a.length > 4 ? ", …" : ""})` : "aucun");
|
|
12
|
+
|
|
13
|
+
/** @param {Array<{ file: string, text: string }>} entries @returns {string} bloc prêt à injecter ("" si le projet n'est pas WordPress) */
|
|
14
|
+
export function wordpressFacts(entries) {
|
|
15
|
+
const php = entries.filter((e) => /\.php$/i.test(e.file));
|
|
16
|
+
const blocks = entries.filter((e) => /(^|\/)block\.json$/.test(e.file));
|
|
17
|
+
const widgets = php.filter((e) => /extends\s+\\?(?:Elementor\\)?Widget_Base\b/.test(e.text));
|
|
18
|
+
const templates = entries.filter((e) => /\.json$/i.test(e.file) && /"elType"\s*:/.test(e.text));
|
|
19
|
+
const theme = entries.some((e) => /(^|\/)style\.css$/.test(e.file) && HEADER.test(e.text.slice(0, 8000)));
|
|
20
|
+
const plugin = php.some((e) => HEADER.test(e.text.slice(0, 8000)));
|
|
21
|
+
const config = php.find((e) => /(^|\/)wp-config\.php$/.test(e.file));
|
|
22
|
+
if (!theme && !plugin && !config && !blocks.length && !widgets.length && !templates.length) return "";
|
|
23
|
+
const own = php.filter((e) => e !== config && !/(^|\/)index\.php$/.test(e.file));
|
|
24
|
+
const noGuard = own.filter((e) => /\b(?:function|class)\s/.test(e.text) && !/ABSPATH|WPINC/.test(e.text)).map((e) => e.file);
|
|
25
|
+
const lines = (/** @type {RegExp[]} */ [bad, ...ok]) => own.flatMap((e) => e.text.split("\n").map((l, i) => ({ at: `${e.file}:${i + 1}`, l })).filter((x) => bad.test(x.l) && !ok.some((o) => o.test(x.l)))).map((x) => x.at);
|
|
26
|
+
const unclean = lines([INPUT, CLEAN]);
|
|
27
|
+
const sql = lines([SQL, /prepare\(/]);
|
|
28
|
+
const ajax = own.filter((e) => /add_action\(\s*["']wp_ajax_/.test(e.text) && !/check_ajax_referer|wp_verify_nonce|current_user_can/.test(e.text)).map((e) => e.file);
|
|
29
|
+
const badBlocks = blocks.filter((b) => { const j = jsonObject(b.text); return typeof j.name !== "string" || j.apiVersion === undefined; }).map((b) => b.file);
|
|
30
|
+
const cfg = config ? [/put your unique phrase here/.test(config.text) && "clés de sécurité non personnalisées", /define\(\s*["']WP_DEBUG["']\s*,\s*true/.test(config.text) && "WP_DEBUG activé", /DB_PASSWORD["']\s*,\s*["'](?:|root|password|123456)["']/.test(config.text) && "mot de passe de base faible ou vide"].filter(Boolean) : [];
|
|
31
|
+
return ["--- WordPress (fichiers du thème et des extensions ; le contenu en base de données n'est pas audité) ---",
|
|
32
|
+
`Détecté : ${[theme && "thème", plugin && "extension", blocks.length && `${blocks.length} bloc(s) Gutenberg (block.json)`, widgets.length && `${widgets.length} widget(s) Elementor`, templates.length && `${templates.length} modèle(s) Elementor exporté(s) en JSON`, config && "wp-config.php"].filter(Boolean).join(", ")}`,
|
|
33
|
+
`- Fichiers PHP sans garde d'accès direct (ABSPATH) : ${list(noGuard)} ; entrées utilisateur non assainies : ${list(unclean)} ; requêtes SQL non préparées : ${list(sql)} ; actions AJAX sans nonce ni contrôle de droits : ${list(ajax)}`,
|
|
34
|
+
`- block.json incomplets (name, apiVersion) : ${list(badBlocks)} ; wp-config.php : ${cfg.length ? cfg.join(", ") : config ? "rien d'anormal repéré" : "absent"}`].join("\n");
|
|
35
|
+
}
|
|
36
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/engine/generate-architecture-guide.js — Boussole technique client :
|
|
2
|
+
// GUIDE_ARCHITECTURE_CODE.md (cartographie des modules, « Où modifier quoi ? »,
|
|
3
|
+
// flux d'exécution). Accents : refactoring → découpage & dépendances ; création → choix initiaux.
|
|
4
|
+
import { DOC_FILES, writeDoc, docHeader } from "./doc-suite.js";
|
|
5
|
+
|
|
6
|
+
// Tableau « Où modifier quoi ? » : une ligne par tâche de la mission.
|
|
7
|
+
/** @param {AutoTask[]} tasks @returns {string} */
|
|
8
|
+
function whereToEditTable(tasks) {
|
|
9
|
+
const rows = tasks.map((t) =>
|
|
10
|
+
`| \`${t.file}\` | ${t.title} | ${t.isNew ? "créé" : "modifié"} | ${t.commit ? "`" + t.commit.slice(0, 7) + "`" : "—"} |`);
|
|
11
|
+
return ["## Où modifier quoi ?", "",
|
|
12
|
+
"| Fichier | Rôle / Objectif | Nature | Commit |", "| :--- | :--- | :--- | :--- |",
|
|
13
|
+
...rows, ""].join("\n");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Section d'accent selon le mode de mission (données réelles du state/mission).
|
|
17
|
+
/** @param {AutoTask[]} tasks @param {MissionPlan | null} mission @param {DocMode} mode @returns {string} */
|
|
18
|
+
function modeSection(tasks, mission, mode) {
|
|
19
|
+
const created = tasks.filter((t) => t.isNew).length;
|
|
20
|
+
if (mode === "creation") {
|
|
21
|
+
const domains = (mission && (mission.domainKeys || []).join(", ")) || "—";
|
|
22
|
+
return "## Choix d'architecture initiaux\n\n" +
|
|
23
|
+
`- Domaines métier identifiés : ${domains}\n` +
|
|
24
|
+
`- Modules créés à l'installation : ${created}/${tasks.length}\n` +
|
|
25
|
+
`- Contraintes de conception posées : ${(mission && mission.constraints || []).length} règles\n`;
|
|
26
|
+
}
|
|
27
|
+
return "## Découpage modulaire & restructuration\n\n" +
|
|
28
|
+
`- Fichiers créés : ${created} — fichiers restructurés : ${tasks.length - created}\n` +
|
|
29
|
+
"- Chaque fichier garde une responsabilité unique (SRP) — voir tableau ci-dessous.\n" +
|
|
30
|
+
"- État des dépendances : tout import pointe vers un module listé ci-dessous.\n";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Génère GUIDE_ARCHITECTURE_CODE.md — n'écrit QUE ce fichier (jamais les deux autres).
|
|
34
|
+
/** @param {string} projectPath @param {AutoState} state @param {MissionPlan | null} mission @param {DocMode} mode @returns {Promise<void>} */
|
|
35
|
+
export async function generateArchitectureGuide(projectPath, state, mission, mode) {
|
|
36
|
+
const flow = state.tasks.map((t, i) =>
|
|
37
|
+
`${i + 1}. \`${t.file}\` — ${t.status}${t.commit ? ` (commit ${t.commit.slice(0, 7)})` : ""}`);
|
|
38
|
+
const body = [
|
|
39
|
+
docHeader("Boussole technique du code", state, mission),
|
|
40
|
+
modeSection(state.tasks, mission, mode),
|
|
41
|
+
whereToEditTable(state.tasks),
|
|
42
|
+
"## Flux d'exécution de la mission", "", ...flow, "",
|
|
43
|
+
].join("\n");
|
|
44
|
+
await writeDoc(projectPath, DOC_FILES.architecture, body);
|
|
45
|
+
}
|
|
46
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/engine/generate-readme.js — Vitrine client : README.md (vue d'ensemble +
|
|
2
|
+
// démarrage rapide). Accents : création → installation & 1er déploiement ;
|
|
3
|
+
// refactoring → état de la structure & reprise après restructuration.
|
|
4
|
+
import { DOC_FILES, writeDoc, docHeader } from "./doc-suite.js";
|
|
5
|
+
|
|
6
|
+
// Section « démarrage rapide » adaptée au mode de mission.
|
|
7
|
+
/** @param {DocMode} mode @returns {string} */
|
|
8
|
+
function quickstart(mode) {
|
|
9
|
+
if (mode === "creation") {
|
|
10
|
+
return "## Démarrage rapide (projet créé de zéro)\n\n" +
|
|
11
|
+
"1. `npm install` — installer les dépendances.\n" +
|
|
12
|
+
"2. `npm test` — vérifier la base de départ au vert.\n" +
|
|
13
|
+
"3. Suivre `RUNBOOK.md` — section « Premier déploiement ».\n";
|
|
14
|
+
}
|
|
15
|
+
return "## Démarrage rapide (structure restructurée)\n\n" +
|
|
16
|
+
"1. `npm test` — confirmer aucune régression après restructuration.\n" +
|
|
17
|
+
"2. Ouvrir `GUIDE_ARCHITECTURE_CODE.md` — cartographie « Où modifier quoi ? ».\n" +
|
|
18
|
+
"3. Consulter `RUNBOOK.md` — procédure de rollback de la mission.\n";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Génère README.md — n'écrit QUE ce fichier (jamais les deux autres).
|
|
22
|
+
/** @param {string} projectPath @param {AutoState} state @param {MissionPlan | null} mission @param {DocMode} mode @returns {Promise<void>} */
|
|
23
|
+
export async function generateReadme(projectPath, state, mission, mode) {
|
|
24
|
+
const roles = (mission && (mission.personas || []).map((p) => p.role_name).join(", ")) || "—";
|
|
25
|
+
const body = [
|
|
26
|
+
docHeader("Vitrine du projet", state, mission),
|
|
27
|
+
"## Vue d'ensemble", "",
|
|
28
|
+
mission && mission.need ? `**Besoin** : ${mission.need}` : "Projet piloté par PipCode (Mode Auto).",
|
|
29
|
+
"", `**Rôles clés** : ${roles}`, "",
|
|
30
|
+
quickstart(mode),
|
|
31
|
+
"## Documents associés", "",
|
|
32
|
+
"- `GUIDE_ARCHITECTURE_CODE.md` — boussole technique du code.",
|
|
33
|
+
"- `RUNBOOK.md` — déploiement, exploitation, rollback.",
|
|
34
|
+
"",
|
|
35
|
+
].join("\n");
|
|
36
|
+
await writeDoc(projectPath, DOC_FILES.readme, body);
|
|
37
|
+
}
|
|
38
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/engine/generate-runbook.js — Manuel d'exploitation client : RUNBOOK.md
|
|
2
|
+
// (variables d'environnement, build/release, healthchecks, logs, rollback).
|
|
3
|
+
// Accents : création → premier déploiement ; refactoring → vérification post-restructuration.
|
|
4
|
+
import { DOC_FILES, writeDoc, docHeader } from "./doc-suite.js";
|
|
5
|
+
|
|
6
|
+
// Procédure de release selon le mode de mission.
|
|
7
|
+
/** @param {DocMode} mode @returns {string} */
|
|
8
|
+
function releaseSection(mode) {
|
|
9
|
+
if (mode === "creation") {
|
|
10
|
+
return "## Premier déploiement\n\n" +
|
|
11
|
+
"1. `npm test` — base au vert.\n" +
|
|
12
|
+
"2. `git tag v0.1.0` — poser le jalon initial.\n" +
|
|
13
|
+
"3. Déployer via votre canal habituel (CI, PaaS…) — vérifier les healthchecks.\n";
|
|
14
|
+
}
|
|
15
|
+
return "## Build & release (post-restructuration)\n\n" +
|
|
16
|
+
"1. `npm test` — confirmer zéro régression de la restructuration.\n" +
|
|
17
|
+
"2. `git tag <version>` — jalon de la restructuration.\n" +
|
|
18
|
+
"3. Déployer ; en cas de régression → section Rollback ci-dessous.\n";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Génère RUNBOOK.md — n'écrit QUE ce fichier (jamais les deux autres).
|
|
22
|
+
/** @param {string} projectPath @param {AutoState} state @param {MissionPlan | null} mission @param {DocMode} mode @returns {Promise<void>} */
|
|
23
|
+
export async function generateRunbook(projectPath, state, mission, mode) {
|
|
24
|
+
const commits = state.tasks.flatMap((t) => (t.commit ? ["`" + t.commit.slice(0, 7) + "`"] : []));
|
|
25
|
+
const body = [
|
|
26
|
+
docHeader("Runbook — déploiement & exploitation", state, mission),
|
|
27
|
+
"## Variables d'environnement", "",
|
|
28
|
+
"| Variable | Rôle | Source |", "| :--- | :--- | :--- |",
|
|
29
|
+
"| À compléter | Variables propres au projet | `.env` / plateforme |", "",
|
|
30
|
+
releaseSection(mode),
|
|
31
|
+
"## Healthchecks", "",
|
|
32
|
+
"- `npm test` — suite au vert avant toute release.",
|
|
33
|
+
"- `git status` — arbre propre (aucun diff non commité).",
|
|
34
|
+
"",
|
|
35
|
+
"## Logs & artefacts PipCode", "",
|
|
36
|
+
"- `.pipcode/mission.md` — journal de la mission (itérations).",
|
|
37
|
+
"- `.pipcode/auto-state.json` — checkpoint (reprise sur crash).",
|
|
38
|
+
"",
|
|
39
|
+
"## Rollback", "",
|
|
40
|
+
`- Commits de la mission (ordre d'exécution) : ${commits.join(", ") || "—"}`,
|
|
41
|
+
"- Retour arrière ciblé : `git revert --no-edit <commit>`.",
|
|
42
|
+
"- Mission entière (si commits contigus) : `git revert --no-edit <premier>^..<dernier>`.",
|
|
43
|
+
"",
|
|
44
|
+
].join("\n");
|
|
45
|
+
await writeDoc(projectPath, DOC_FILES.runbook, body);
|
|
46
|
+
}
|
|
47
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/engine/git-facts.js — Faits Git réels pour l'audit (lecture seule) : dépôt présent ?, modifications non validées, derniers commits.
|
|
2
|
+
// git via execFile sans shell ; tout échec (git absent, pas un dépôt) → constat honnête, jamais d'exception.
|
|
3
|
+
import { gitExec } from "../auto/git.js";
|
|
4
|
+
|
|
5
|
+
const MAX_LINES = 20;
|
|
6
|
+
|
|
7
|
+
/** @param {string} projectPath @returns {Promise<string>} bloc texte prêt à injecter */
|
|
8
|
+
export async function gitFacts(projectPath) {
|
|
9
|
+
const inside = await gitExec(projectPath, ["rev-parse", "--is-inside-work-tree"]).catch(() => "");
|
|
10
|
+
if (inside !== "true") return "--- État Git ---\nAucun dépôt Git détecté dans ce dossier (ou git indisponible).";
|
|
11
|
+
const status = (await gitExec(projectPath, ["status", "--porcelain"]).catch(() => "")).split("\n").filter(Boolean);
|
|
12
|
+
const log = (await gitExec(projectPath, ["log", "-n", "8", "--pretty=format:%h %s"]).catch(() => "")).split("\n").filter(Boolean);
|
|
13
|
+
const changes = status.length ? `${status.length} fichier(s) modifié(s) ou non suivi(s) :\n${status.slice(0, MAX_LINES).join("\n")}${status.length > MAX_LINES ? `\n[… ${status.length - MAX_LINES} autres]` : ""}` : "Arbre de travail propre (aucune modification non validée).";
|
|
14
|
+
return `--- État Git ---\nDépôt Git présent.\n${changes}\nDerniers commits :\n${log.length ? log.join("\n") : "(aucun commit)"}`;
|
|
15
|
+
}
|
|
16
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/engine/history-purge.js — Le raisonnement des modèles n'est JAMAIS réinjecté dans un prompt : blocs repliables « :::steps … ::: » du chat
|
|
2
|
+
// (non fermé = jusqu'à la fin) et balises <think>. Préserve la fenêtre d'attention et le budget de tokens. Pur.
|
|
3
|
+
import { stripReasoning } from "../core/dual-stream-parser.js";
|
|
4
|
+
|
|
5
|
+
/** @param {string} text @returns {string} */
|
|
6
|
+
export function purgeReasoning(text) {
|
|
7
|
+
const kept = [];
|
|
8
|
+
let inside = false;
|
|
9
|
+
for (const line of String(text).split("\n")) {
|
|
10
|
+
if (!inside && line.trim() === ":::steps") { inside = true; continue; }
|
|
11
|
+
if (inside) { if (line.trim() === ":::") inside = false; continue; }
|
|
12
|
+
kept.push(line);
|
|
13
|
+
}
|
|
14
|
+
return stripReasoning(kept.join("\n"));
|
|
15
|
+
}
|
|
16
|
+
// MODULE_COMPLETE
|