cap-pro 1.0.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/.claude-plugin/README.md +26 -0
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/LICENSE +21 -0
- package/README.ja-JP.md +834 -0
- package/README.ko-KR.md +823 -0
- package/README.md +806 -0
- package/README.pt-BR.md +452 -0
- package/README.zh-CN.md +800 -0
- package/agents/cap-architect.md +269 -0
- package/agents/cap-brainstormer.md +207 -0
- package/agents/cap-curator.md +276 -0
- package/agents/cap-debugger.md +365 -0
- package/agents/cap-designer.md +246 -0
- package/agents/cap-historian.md +464 -0
- package/agents/cap-migrator.md +291 -0
- package/agents/cap-prototyper.md +197 -0
- package/agents/cap-validator.md +308 -0
- package/bin/install.js +5433 -0
- package/cap/bin/cap-tools.cjs +853 -0
- package/cap/bin/lib/arc-scanner.cjs +344 -0
- package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
- package/cap/bin/lib/cap-anchor.cjs +228 -0
- package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
- package/cap/bin/lib/cap-checkpoint.cjs +434 -0
- package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
- package/cap/bin/lib/cap-cluster-display.cjs +52 -0
- package/cap/bin/lib/cap-cluster-format.cjs +245 -0
- package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
- package/cap/bin/lib/cap-cluster-io.cjs +212 -0
- package/cap/bin/lib/cap-completeness.cjs +540 -0
- package/cap/bin/lib/cap-deps.cjs +583 -0
- package/cap/bin/lib/cap-design-families.cjs +332 -0
- package/cap/bin/lib/cap-design.cjs +966 -0
- package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
- package/cap/bin/lib/cap-doctor.cjs +752 -0
- package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
- package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
- package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
- package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
- package/cap/bin/lib/cap-feature-map.cjs +1943 -0
- package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
- package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
- package/cap/bin/lib/cap-learn-review.cjs +1072 -0
- package/cap/bin/lib/cap-learning-signals.cjs +627 -0
- package/cap/bin/lib/cap-loader.cjs +227 -0
- package/cap/bin/lib/cap-logger.cjs +57 -0
- package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
- package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
- package/cap/bin/lib/cap-memory-dir.cjs +987 -0
- package/cap/bin/lib/cap-memory-engine.cjs +698 -0
- package/cap/bin/lib/cap-memory-extends.cjs +398 -0
- package/cap/bin/lib/cap-memory-graph.cjs +790 -0
- package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
- package/cap/bin/lib/cap-memory-pin.cjs +183 -0
- package/cap/bin/lib/cap-memory-platform.cjs +490 -0
- package/cap/bin/lib/cap-memory-prune.cjs +707 -0
- package/cap/bin/lib/cap-memory-schema.cjs +812 -0
- package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
- package/cap/bin/lib/cap-migrate.cjs +540 -0
- package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
- package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
- package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
- package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
- package/cap/bin/lib/cap-reconcile.cjs +570 -0
- package/cap/bin/lib/cap-research-gate.cjs +218 -0
- package/cap/bin/lib/cap-scope-filter.cjs +402 -0
- package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
- package/cap/bin/lib/cap-session-extract.cjs +987 -0
- package/cap/bin/lib/cap-session.cjs +445 -0
- package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
- package/cap/bin/lib/cap-stack-docs.cjs +646 -0
- package/cap/bin/lib/cap-tag-observer.cjs +371 -0
- package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
- package/cap/bin/lib/cap-telemetry.cjs +466 -0
- package/cap/bin/lib/cap-test-audit.cjs +1438 -0
- package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
- package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
- package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
- package/cap/bin/lib/cap-trace.cjs +399 -0
- package/cap/bin/lib/cap-trust-mode.cjs +336 -0
- package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
- package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
- package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
- package/cap/bin/lib/cap-ui.cjs +1245 -0
- package/cap/bin/lib/cap-upgrade.cjs +1028 -0
- package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
- package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
- package/cap/bin/lib/cli/init-router.cjs +68 -0
- package/cap/bin/lib/cli/phase-router.cjs +102 -0
- package/cap/bin/lib/cli/state-router.cjs +61 -0
- package/cap/bin/lib/cli/template-router.cjs +37 -0
- package/cap/bin/lib/cli/uat-router.cjs +29 -0
- package/cap/bin/lib/cli/validation-router.cjs +26 -0
- package/cap/bin/lib/cli/verification-router.cjs +31 -0
- package/cap/bin/lib/cli/workstream-router.cjs +39 -0
- package/cap/bin/lib/commands.cjs +961 -0
- package/cap/bin/lib/config.cjs +467 -0
- package/cap/bin/lib/convention-reader.cjs +258 -0
- package/cap/bin/lib/core.cjs +1241 -0
- package/cap/bin/lib/feature-aggregator.cjs +423 -0
- package/cap/bin/lib/frontmatter.cjs +337 -0
- package/cap/bin/lib/init.cjs +1443 -0
- package/cap/bin/lib/manifest-generator.cjs +383 -0
- package/cap/bin/lib/milestone.cjs +253 -0
- package/cap/bin/lib/model-profiles.cjs +69 -0
- package/cap/bin/lib/monorepo-context.cjs +226 -0
- package/cap/bin/lib/monorepo-migrator.cjs +509 -0
- package/cap/bin/lib/phase.cjs +889 -0
- package/cap/bin/lib/profile-output.cjs +989 -0
- package/cap/bin/lib/profile-pipeline.cjs +540 -0
- package/cap/bin/lib/roadmap.cjs +330 -0
- package/cap/bin/lib/security.cjs +394 -0
- package/cap/bin/lib/session-manager.cjs +292 -0
- package/cap/bin/lib/skeleton-generator.cjs +179 -0
- package/cap/bin/lib/state.cjs +1032 -0
- package/cap/bin/lib/template.cjs +231 -0
- package/cap/bin/lib/test-detector.cjs +62 -0
- package/cap/bin/lib/uat.cjs +283 -0
- package/cap/bin/lib/verify.cjs +889 -0
- package/cap/bin/lib/workspace-detector.cjs +371 -0
- package/cap/bin/lib/workstream.cjs +492 -0
- package/cap/commands/gsd/workstreams.md +63 -0
- package/cap/references/arc-standard.md +315 -0
- package/cap/references/cap-agent-architecture.md +101 -0
- package/cap/references/cap-gitignore-template +9 -0
- package/cap/references/cap-zero-deps.md +158 -0
- package/cap/references/checkpoints.md +778 -0
- package/cap/references/continuation-format.md +249 -0
- package/cap/references/contract-test-templates.md +312 -0
- package/cap/references/feature-map-template.md +25 -0
- package/cap/references/git-integration.md +295 -0
- package/cap/references/git-planning-commit.md +38 -0
- package/cap/references/model-profiles.md +174 -0
- package/cap/references/phase-numbering.md +126 -0
- package/cap/references/planning-config.md +202 -0
- package/cap/references/property-test-templates.md +316 -0
- package/cap/references/security-test-templates.md +347 -0
- package/cap/references/session-template.json +8 -0
- package/cap/references/tdd.md +263 -0
- package/cap/references/user-profiling.md +681 -0
- package/cap/references/verification-patterns.md +612 -0
- package/cap/templates/UAT.md +265 -0
- package/cap/templates/claude-md.md +175 -0
- package/cap/templates/codebase/architecture.md +255 -0
- package/cap/templates/codebase/concerns.md +310 -0
- package/cap/templates/codebase/conventions.md +307 -0
- package/cap/templates/codebase/integrations.md +280 -0
- package/cap/templates/codebase/stack.md +186 -0
- package/cap/templates/codebase/structure.md +285 -0
- package/cap/templates/codebase/testing.md +480 -0
- package/cap/templates/config.json +44 -0
- package/cap/templates/context.md +352 -0
- package/cap/templates/continue-here.md +78 -0
- package/cap/templates/copilot-instructions.md +7 -0
- package/cap/templates/debug-subagent-prompt.md +91 -0
- package/cap/templates/discussion-log.md +63 -0
- package/cap/templates/milestone-archive.md +123 -0
- package/cap/templates/milestone.md +115 -0
- package/cap/templates/phase-prompt.md +610 -0
- package/cap/templates/planner-subagent-prompt.md +117 -0
- package/cap/templates/project.md +186 -0
- package/cap/templates/requirements.md +231 -0
- package/cap/templates/research-project/ARCHITECTURE.md +204 -0
- package/cap/templates/research-project/FEATURES.md +147 -0
- package/cap/templates/research-project/PITFALLS.md +200 -0
- package/cap/templates/research-project/STACK.md +120 -0
- package/cap/templates/research-project/SUMMARY.md +170 -0
- package/cap/templates/research.md +552 -0
- package/cap/templates/roadmap.md +202 -0
- package/cap/templates/state.md +176 -0
- package/cap/templates/summary.md +364 -0
- package/cap/templates/user-preferences.md +498 -0
- package/cap/templates/verification-report.md +322 -0
- package/cap/workflows/add-phase.md +112 -0
- package/cap/workflows/add-tests.md +351 -0
- package/cap/workflows/add-todo.md +158 -0
- package/cap/workflows/audit-milestone.md +340 -0
- package/cap/workflows/audit-uat.md +109 -0
- package/cap/workflows/autonomous.md +891 -0
- package/cap/workflows/check-todos.md +177 -0
- package/cap/workflows/cleanup.md +152 -0
- package/cap/workflows/complete-milestone.md +767 -0
- package/cap/workflows/diagnose-issues.md +231 -0
- package/cap/workflows/discovery-phase.md +289 -0
- package/cap/workflows/discuss-phase-assumptions.md +653 -0
- package/cap/workflows/discuss-phase.md +1049 -0
- package/cap/workflows/do.md +104 -0
- package/cap/workflows/execute-phase.md +846 -0
- package/cap/workflows/execute-plan.md +514 -0
- package/cap/workflows/fast.md +105 -0
- package/cap/workflows/forensics.md +265 -0
- package/cap/workflows/health.md +181 -0
- package/cap/workflows/help.md +660 -0
- package/cap/workflows/insert-phase.md +130 -0
- package/cap/workflows/list-phase-assumptions.md +178 -0
- package/cap/workflows/list-workspaces.md +56 -0
- package/cap/workflows/manager.md +362 -0
- package/cap/workflows/map-codebase.md +377 -0
- package/cap/workflows/milestone-summary.md +223 -0
- package/cap/workflows/new-milestone.md +486 -0
- package/cap/workflows/new-project.md +1250 -0
- package/cap/workflows/new-workspace.md +237 -0
- package/cap/workflows/next.md +97 -0
- package/cap/workflows/node-repair.md +92 -0
- package/cap/workflows/note.md +156 -0
- package/cap/workflows/pause-work.md +176 -0
- package/cap/workflows/plan-milestone-gaps.md +273 -0
- package/cap/workflows/plan-phase.md +857 -0
- package/cap/workflows/plant-seed.md +169 -0
- package/cap/workflows/pr-branch.md +129 -0
- package/cap/workflows/profile-user.md +449 -0
- package/cap/workflows/progress.md +507 -0
- package/cap/workflows/quick.md +757 -0
- package/cap/workflows/remove-phase.md +155 -0
- package/cap/workflows/remove-workspace.md +90 -0
- package/cap/workflows/research-phase.md +82 -0
- package/cap/workflows/resume-project.md +326 -0
- package/cap/workflows/review.md +228 -0
- package/cap/workflows/session-report.md +146 -0
- package/cap/workflows/settings.md +283 -0
- package/cap/workflows/ship.md +228 -0
- package/cap/workflows/stats.md +60 -0
- package/cap/workflows/transition.md +671 -0
- package/cap/workflows/ui-phase.md +298 -0
- package/cap/workflows/ui-review.md +161 -0
- package/cap/workflows/update.md +323 -0
- package/cap/workflows/validate-phase.md +170 -0
- package/cap/workflows/verify-phase.md +254 -0
- package/cap/workflows/verify-work.md +637 -0
- package/commands/cap/annotate.md +165 -0
- package/commands/cap/brainstorm.md +393 -0
- package/commands/cap/checkpoint.md +106 -0
- package/commands/cap/completeness.md +94 -0
- package/commands/cap/continue.md +72 -0
- package/commands/cap/debug.md +588 -0
- package/commands/cap/deps.md +169 -0
- package/commands/cap/design.md +479 -0
- package/commands/cap/init.md +354 -0
- package/commands/cap/iterate.md +249 -0
- package/commands/cap/learn.md +459 -0
- package/commands/cap/memory.md +275 -0
- package/commands/cap/migrate-feature-map.md +91 -0
- package/commands/cap/migrate-memory.md +108 -0
- package/commands/cap/migrate-tags.md +91 -0
- package/commands/cap/migrate.md +131 -0
- package/commands/cap/prototype.md +510 -0
- package/commands/cap/reconcile.md +121 -0
- package/commands/cap/review.md +360 -0
- package/commands/cap/save.md +72 -0
- package/commands/cap/scan.md +404 -0
- package/commands/cap/start.md +356 -0
- package/commands/cap/status.md +118 -0
- package/commands/cap/test-audit.md +262 -0
- package/commands/cap/test.md +394 -0
- package/commands/cap/trace.md +133 -0
- package/commands/cap/ui.md +167 -0
- package/hooks/dist/cap-check-update.js +115 -0
- package/hooks/dist/cap-context-monitor.js +185 -0
- package/hooks/dist/cap-learn-review-hook.js +114 -0
- package/hooks/dist/cap-learning-hook.js +192 -0
- package/hooks/dist/cap-memory.js +299 -0
- package/hooks/dist/cap-prompt-guard.js +97 -0
- package/hooks/dist/cap-statusline.js +157 -0
- package/hooks/dist/cap-tag-observer.js +115 -0
- package/hooks/dist/cap-version-check.js +112 -0
- package/hooks/dist/cap-workflow-guard.js +175 -0
- package/hooks/hooks.json +55 -0
- package/package.json +58 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +93 -0
- package/scripts/cap-removal-checklist.md +202 -0
- package/scripts/prompt-injection-scan.sh +199 -0
- package/scripts/run-tests.cjs +181 -0
- package/scripts/secret-scan.sh +227 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// @cap-feature(feature:F-075, primary:true) Trust-Mode Configuration Slot — open-closed for future B/C activation.
|
|
2
|
+
// @cap-context Trust-Mode is the first-class config axis for V5-true Self-Learning. MVP only enables A.
|
|
3
|
+
// F-071 (Pattern Extraction), F-073 (Review Board), F-074 (Apply/Unlearn) will consume this helper
|
|
4
|
+
// via getTrustMode() + requireApproval() so B/C activation is a library-internal change only.
|
|
5
|
+
// @cap-decision(F-075/D1) .cap/config.json is source-of-truth for trustMode; SESSION.json mirrors per-session.
|
|
6
|
+
// On divergence, config wins and SESSION.json is aligned on next read.
|
|
7
|
+
// @cap-decision(F-075/D2) getTrustMode returns { mode, source, degraded? } for debug transparency.
|
|
8
|
+
// Shape is frozen from MVP onward — B/C activation MUST NOT change the return shape.
|
|
9
|
+
// @cap-decision(F-075/D3) requireApproval(projectRoot) is the open-closed extension point for B/C.
|
|
10
|
+
// MVP: always true. Future B: scope-aware. Future C: returns false.
|
|
11
|
+
// @cap-decision(F-075/D4) Non-A degradation warning rate-limited to 1× per process via in-memory Set-memo.
|
|
12
|
+
// Keyed by projectRoot + raw value so distinct degradations still log once each.
|
|
13
|
+
// @cap-constraint Zero external dependencies — uses only Node.js built-ins (fs, path, os, crypto).
|
|
14
|
+
// @cap-pattern All trust-mode reads/writes go through this module — no other code touches trustMode directly.
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const crypto = require('node:crypto');
|
|
21
|
+
|
|
22
|
+
// -----------------------------------------------------------------------------
|
|
23
|
+
// Constants
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const CAP_DIR = '.cap';
|
|
27
|
+
const CONFIG_FILE = 'config.json';
|
|
28
|
+
|
|
29
|
+
// @cap-decision(F-075/D2) VALID_MODES is the single authoritative set. Adding D/E here
|
|
30
|
+
// is the ONLY code change needed when the mode axis ever extends.
|
|
31
|
+
const VALID_MODES = Object.freeze(['A', 'B', 'C']);
|
|
32
|
+
const DEFAULT_MODE = 'A';
|
|
33
|
+
|
|
34
|
+
// @cap-decision(F-075/D5) Every TrustModeResult returned to a caller is Object.freeze-d at the
|
|
35
|
+
// boundary. Consumers (F-071/F-073/F-074) must not be able to mutate the
|
|
36
|
+
// object in a way that alters later reads or a shared memo. Defense-in-depth
|
|
37
|
+
// for the open-closed contract: even buggy consumer code can't silently
|
|
38
|
+
// flip mode to 'C' mid-pipeline.
|
|
39
|
+
function _frozenResult(obj) {
|
|
40
|
+
return Object.freeze(obj);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// @cap-decision(F-075/D4) Warning rate-limit memo is process-local (not persisted).
|
|
44
|
+
// Each fresh node process logs once per (projectRoot, rawValue) combo.
|
|
45
|
+
const _warnOnceMemo = new Set();
|
|
46
|
+
|
|
47
|
+
const WARN_CODE = 'trust-mode-not-implemented';
|
|
48
|
+
|
|
49
|
+
// -----------------------------------------------------------------------------
|
|
50
|
+
// Types
|
|
51
|
+
// -----------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {'A'|'B'|'C'} TrustMode
|
|
55
|
+
*
|
|
56
|
+
* @typedef {Object} TrustModeResult
|
|
57
|
+
* @property {TrustMode} mode - Effective trust mode (always 'A' in MVP).
|
|
58
|
+
* @property {'config'|'session'|'default'} source - Where the value came from.
|
|
59
|
+
* @property {boolean} [degraded] - Present and true when a non-A value was read and degraded to A.
|
|
60
|
+
* @property {*} [rawValue] - The raw value seen in config/session when degraded.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
// -----------------------------------------------------------------------------
|
|
64
|
+
// Validation
|
|
65
|
+
// -----------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
// @cap-todo(ac:F-075/AC-3) Non-A degradation — isValidMode is the gate that triggers the downgrade branch.
|
|
68
|
+
/**
|
|
69
|
+
* Check whether `value` is one of the allowed trust-mode strings.
|
|
70
|
+
* Strict equality — no lowercasing, no trimming. 'a' is NOT valid, only 'A'.
|
|
71
|
+
* @param {*} value
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
function isValidMode(value) {
|
|
75
|
+
return typeof value === 'string' && VALID_MODES.includes(value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// -----------------------------------------------------------------------------
|
|
79
|
+
// Config I/O (private)
|
|
80
|
+
// -----------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Read .cap/config.json. Returns `{}` when missing or malformed (no throw).
|
|
84
|
+
* Mirrors cap-telemetry's readConfig pattern for behavioural consistency.
|
|
85
|
+
* @param {string} projectRoot
|
|
86
|
+
* @returns {object}
|
|
87
|
+
*/
|
|
88
|
+
function _readConfig(projectRoot) {
|
|
89
|
+
const configPath = path.join(projectRoot, CAP_DIR, CONFIG_FILE);
|
|
90
|
+
try {
|
|
91
|
+
if (!fs.existsSync(configPath)) return {};
|
|
92
|
+
const raw = fs.readFileSync(configPath, 'utf8');
|
|
93
|
+
const parsed = JSON.parse(raw);
|
|
94
|
+
// @cap-decision(F-075/D1) Non-object roots (strings, arrays, null) normalise to {} so that
|
|
95
|
+
// cfg.trustMode cannot throw. Matches cap-telemetry D5.
|
|
96
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
|
|
97
|
+
return parsed;
|
|
98
|
+
} catch (_e) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// @cap-todo(ac:F-075/AC-2) Persist trustMode across sessions via atomic write to .cap/config.json.
|
|
104
|
+
/**
|
|
105
|
+
* Atomically write `config` to .cap/config.json. Creates .cap/ if needed.
|
|
106
|
+
* Uses temp+rename so a crashed write cannot leave a half-written JSON file.
|
|
107
|
+
* @param {string} projectRoot
|
|
108
|
+
* @param {object} config
|
|
109
|
+
*/
|
|
110
|
+
function _writeConfig(projectRoot, config) {
|
|
111
|
+
const capDir = path.join(projectRoot, CAP_DIR);
|
|
112
|
+
if (!fs.existsSync(capDir)) fs.mkdirSync(capDir, { recursive: true });
|
|
113
|
+
|
|
114
|
+
const configPath = path.join(capDir, CONFIG_FILE);
|
|
115
|
+
// Unique tmp suffix avoids clashes under concurrent writes from the same process.
|
|
116
|
+
const tmpPath = path.join(
|
|
117
|
+
capDir,
|
|
118
|
+
`.${CONFIG_FILE}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`,
|
|
119
|
+
);
|
|
120
|
+
const payload = JSON.stringify(config, null, 2) + '\n';
|
|
121
|
+
fs.writeFileSync(tmpPath, payload, 'utf8');
|
|
122
|
+
fs.renameSync(tmpPath, configPath);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// -----------------------------------------------------------------------------
|
|
126
|
+
// Warning emission (private)
|
|
127
|
+
// -----------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
// @cap-todo(ac:F-075/AC-3) Warning rate-limit — one stderr line per (projectRoot, rawValue) per process.
|
|
130
|
+
/**
|
|
131
|
+
* Emit a single-line stderr warning for non-A degradation, rate-limited to once per
|
|
132
|
+
* (projectRoot, rawValue) combination per Node process.
|
|
133
|
+
* @param {string} projectRoot
|
|
134
|
+
* @param {*} rawValue
|
|
135
|
+
*/
|
|
136
|
+
function _warnOnce(projectRoot, rawValue) {
|
|
137
|
+
// JSON.stringify is the cheapest stable key; handles objects, nulls, numbers, booleans.
|
|
138
|
+
let keyPart;
|
|
139
|
+
try {
|
|
140
|
+
keyPart = JSON.stringify(rawValue);
|
|
141
|
+
} catch (_e) {
|
|
142
|
+
keyPart = String(rawValue);
|
|
143
|
+
}
|
|
144
|
+
const memoKey = `${projectRoot}::${keyPart}`;
|
|
145
|
+
if (_warnOnceMemo.has(memoKey)) return;
|
|
146
|
+
_warnOnceMemo.add(memoKey);
|
|
147
|
+
|
|
148
|
+
const ts = new Date().toISOString();
|
|
149
|
+
const displayValue = keyPart === undefined ? 'undefined' : keyPart;
|
|
150
|
+
// Single line — grep-friendly. Code token is stable and documented.
|
|
151
|
+
// eslint-disable-next-line no-console
|
|
152
|
+
console.error(
|
|
153
|
+
`[${ts}] ${WARN_CODE} trustMode=${displayValue} — MVP only supports 'A'; degrading to 'A'.`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Test-only hook to clear the warn-once memo so unit tests can assert rate-limit behaviour
|
|
159
|
+
* across multiple invocations without spawning child processes.
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
function _resetWarnOnceForTests() {
|
|
163
|
+
_warnOnceMemo.clear();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// -----------------------------------------------------------------------------
|
|
167
|
+
// Session sync (private)
|
|
168
|
+
// -----------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
// @cap-todo(ac:F-075/AC-1) SESSION.json must carry trustMode field; this helper aligns it with config.
|
|
171
|
+
/**
|
|
172
|
+
* Align SESSION.json.trustMode with the resolved mode, if SESSION.json exists.
|
|
173
|
+
* Failures are silent — SESSION.json is ephemeral and its absence/corruption must
|
|
174
|
+
* never break the read path. Uses cap-session via lazy require to avoid a hard
|
|
175
|
+
* cycle (cap-session re-exports getTrustMode for convenience).
|
|
176
|
+
* @param {string} projectRoot
|
|
177
|
+
* @param {TrustMode} resolvedMode
|
|
178
|
+
*/
|
|
179
|
+
function _syncSession(projectRoot, resolvedMode) {
|
|
180
|
+
try {
|
|
181
|
+
// Lazy require — cap-session may later want to import cap-trust-mode; keep the
|
|
182
|
+
// edge loose so require-order never matters.
|
|
183
|
+
const session = require('./cap-session.cjs');
|
|
184
|
+
const current = session.loadSession(projectRoot);
|
|
185
|
+
if (current.trustMode !== resolvedMode) {
|
|
186
|
+
session.updateSession(projectRoot, { trustMode: resolvedMode });
|
|
187
|
+
}
|
|
188
|
+
} catch (_e) {
|
|
189
|
+
// Silent — SESSION mirror is a debug affordance, not a correctness contract.
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// -----------------------------------------------------------------------------
|
|
194
|
+
// Public API
|
|
195
|
+
// -----------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
// @cap-todo(ac:F-075/AC-1) SESSION.json trustMode field — this helper is the read path.
|
|
198
|
+
// @cap-todo(ac:F-075/AC-2) .cap/config.json persistence — config is read as source-of-truth.
|
|
199
|
+
// @cap-todo(ac:F-075/AC-3) Non-A degradation — invalid values are coerced to 'A' with rate-limited warning.
|
|
200
|
+
// @cap-todo(ac:F-075/AC-5) Determinism — pure function of on-disk state; same inputs, same output.
|
|
201
|
+
// @cap-todo(ac:F-075/AC-6) Single helper exposed to all Learn-Pipeline consumers (F-071, F-073, F-074).
|
|
202
|
+
// @cap-todo(ac:F-075/AC-7) Open-closed — only the return value of this function changes for B/C activation;
|
|
203
|
+
// the shape stays frozen. Feature code MUST NOT read config/session directly.
|
|
204
|
+
/**
|
|
205
|
+
* Resolve the effective trust mode for `projectRoot`.
|
|
206
|
+
*
|
|
207
|
+
* Precedence:
|
|
208
|
+
* 1. .cap/config.json → `trustMode` (source-of-truth)
|
|
209
|
+
* 2. SESSION.json → `trustMode` (per-session cache)
|
|
210
|
+
* 3. DEFAULT_MODE ('A')
|
|
211
|
+
*
|
|
212
|
+
* Any non-valid mode is degraded to 'A' with `degraded:true` and a rate-limited
|
|
213
|
+
* stderr warning. In the MVP this always resolves to 'A'.
|
|
214
|
+
*
|
|
215
|
+
* @param {string} projectRoot - Absolute path to project root.
|
|
216
|
+
* @returns {TrustModeResult}
|
|
217
|
+
*/
|
|
218
|
+
function getTrustMode(projectRoot) {
|
|
219
|
+
const cfg = _readConfig(projectRoot);
|
|
220
|
+
|
|
221
|
+
// 1. Config-sourced
|
|
222
|
+
if (Object.prototype.hasOwnProperty.call(cfg, 'trustMode')) {
|
|
223
|
+
const raw = cfg.trustMode;
|
|
224
|
+
if (isValidMode(raw)) {
|
|
225
|
+
// @cap-decision(F-075/D2) MVP hard-caps to A even when B/C is a syntactically valid mode,
|
|
226
|
+
// so accidental config edits do not silently unlock unsupported behaviour.
|
|
227
|
+
// When B/C is implemented, drop the cap but keep the shape.
|
|
228
|
+
if (raw === DEFAULT_MODE) {
|
|
229
|
+
_syncSession(projectRoot, DEFAULT_MODE);
|
|
230
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'config' });
|
|
231
|
+
}
|
|
232
|
+
// raw is 'B' or 'C' — degrade with warning
|
|
233
|
+
_warnOnce(projectRoot, raw);
|
|
234
|
+
_syncSession(projectRoot, DEFAULT_MODE);
|
|
235
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'config', degraded: true, rawValue: raw });
|
|
236
|
+
}
|
|
237
|
+
// Invalid (wrong type/case/etc.) — degrade with warning
|
|
238
|
+
_warnOnce(projectRoot, raw);
|
|
239
|
+
_syncSession(projectRoot, DEFAULT_MODE);
|
|
240
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'config', degraded: true, rawValue: raw });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 2. Session-sourced — only counts when SESSION.json actually exists on disk,
|
|
244
|
+
// otherwise loadSession() returns the default stub and we'd mis-attribute the source.
|
|
245
|
+
const sessionPath = path.join(projectRoot, CAP_DIR, 'SESSION.json');
|
|
246
|
+
if (fs.existsSync(sessionPath)) {
|
|
247
|
+
try {
|
|
248
|
+
const session = require('./cap-session.cjs');
|
|
249
|
+
const sess = session.loadSession(projectRoot);
|
|
250
|
+
if (Object.prototype.hasOwnProperty.call(sess, 'trustMode') && sess.trustMode != null) {
|
|
251
|
+
const raw = sess.trustMode;
|
|
252
|
+
if (isValidMode(raw) && raw === DEFAULT_MODE) {
|
|
253
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'session' });
|
|
254
|
+
}
|
|
255
|
+
// Valid-but-unsupported (B/C) or plain invalid — both degrade with warning.
|
|
256
|
+
_warnOnce(projectRoot, raw);
|
|
257
|
+
_syncSession(projectRoot, DEFAULT_MODE);
|
|
258
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'session', degraded: true, rawValue: raw });
|
|
259
|
+
}
|
|
260
|
+
} catch (_e) {
|
|
261
|
+
// cap-session unavailable — fall through to default.
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 3. Default
|
|
266
|
+
return _frozenResult({ mode: DEFAULT_MODE, source: 'default' });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// @cap-todo(ac:F-075/AC-2) Persistence writer — atomic write to .cap/config.json.
|
|
270
|
+
/**
|
|
271
|
+
* Persist `mode` into .cap/config.json (source-of-truth) and mirror into SESSION.json.
|
|
272
|
+
* Rejects unknown modes with a thrown Error — this is a developer-facing setter, not a
|
|
273
|
+
* silent coercion path. Use `getTrustMode` for the silent-degrade read path.
|
|
274
|
+
*
|
|
275
|
+
* @param {string} projectRoot
|
|
276
|
+
* @param {TrustMode} mode
|
|
277
|
+
* @returns {TrustModeResult}
|
|
278
|
+
*/
|
|
279
|
+
function setTrustMode(projectRoot, mode) {
|
|
280
|
+
if (!isValidMode(mode)) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`setTrustMode: invalid mode ${JSON.stringify(mode)}. Expected one of ${VALID_MODES.join(', ')}.`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
const cfg = _readConfig(projectRoot);
|
|
286
|
+
cfg.trustMode = mode;
|
|
287
|
+
_writeConfig(projectRoot, cfg);
|
|
288
|
+
_syncSession(projectRoot, DEFAULT_MODE);
|
|
289
|
+
// Read-back through getTrustMode to get the same shape every caller sees,
|
|
290
|
+
// including the degraded flag for B/C in the MVP.
|
|
291
|
+
return getTrustMode(projectRoot);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// @cap-todo(ac:F-075/AC-4) Human-in-the-Loop approval gate — MVP always requires approval.
|
|
295
|
+
// @cap-todo(ac:F-075/AC-7) Open-closed extension point — B/C activation changes only this return.
|
|
296
|
+
/**
|
|
297
|
+
* Return true iff the caller must solicit a human approval before applying a
|
|
298
|
+
* Learn-Pipeline write (pattern extraction, patch apply, unlearn).
|
|
299
|
+
*
|
|
300
|
+
* MVP contract:
|
|
301
|
+
* - Mode A → true (always require approval)
|
|
302
|
+
* - Mode B → future: scope-aware; stub returns true (safe default)
|
|
303
|
+
* - Mode C → future: false (fully autonomous)
|
|
304
|
+
*
|
|
305
|
+
* Callers MUST NOT inspect `trustMode` directly. They ask this helper.
|
|
306
|
+
*
|
|
307
|
+
* @param {string} projectRoot
|
|
308
|
+
* @param {{ scope?: string }} [_opts] - Reserved for Mode B scope resolution.
|
|
309
|
+
* @returns {boolean}
|
|
310
|
+
*/
|
|
311
|
+
function requireApproval(projectRoot, _opts) {
|
|
312
|
+
const { mode } = getTrustMode(projectRoot);
|
|
313
|
+
// MVP: every non-C mode is approval-required. Since mode is always 'A' today,
|
|
314
|
+
// this always returns true. The shape of the conditional is kept to make the
|
|
315
|
+
// future B/C activation a one-liner edit.
|
|
316
|
+
if (mode === 'C') return false;
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// -----------------------------------------------------------------------------
|
|
321
|
+
// Exports
|
|
322
|
+
// -----------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
// Public API
|
|
326
|
+
getTrustMode,
|
|
327
|
+
setTrustMode,
|
|
328
|
+
requireApproval,
|
|
329
|
+
isValidMode,
|
|
330
|
+
// Constants (read-only — frozen arrays)
|
|
331
|
+
VALID_MODES,
|
|
332
|
+
DEFAULT_MODE,
|
|
333
|
+
WARN_CODE,
|
|
334
|
+
// Test hooks (underscore-prefixed; not part of the stable API)
|
|
335
|
+
_resetWarnOnceForTests,
|
|
336
|
+
};
|