@principles/pd-cli 1.132.0 → 1.134.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/dist/commands/__tests__/intent-flag-wiring.test.js +74 -3
- package/dist/commands/__tests__/intent-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +2 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +1 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +60 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +1 -0
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js +11 -4
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +9 -0
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +1 -1
- package/dist/commands/admission-gate.d.ts +24 -0
- package/dist/commands/admission-gate.d.ts.map +1 -0
- package/dist/commands/admission-gate.js +29 -0
- package/dist/commands/admission-gate.js.map +1 -0
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +72 -6
- package/dist/commands/candidate.js.map +1 -1
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js +40 -0
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/demo-story-a.d.ts.map +1 -1
- package/dist/commands/demo-story-a.js +4 -2
- package/dist/commands/demo-story-a.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +155 -8
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/errors-list.d.ts +25 -0
- package/dist/commands/errors-list.d.ts.map +1 -0
- package/dist/commands/errors-list.js +368 -0
- package/dist/commands/errors-list.js.map +1 -0
- package/dist/commands/health-codex.d.ts +7 -0
- package/dist/commands/health-codex.d.ts.map +1 -0
- package/dist/commands/health-codex.js +209 -0
- package/dist/commands/health-codex.js.map +1 -0
- package/dist/commands/health.d.ts +24 -0
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +48 -0
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/intent.d.ts +21 -4
- package/dist/commands/intent.d.ts.map +1 -1
- package/dist/commands/intent.js +88 -27
- package/dist/commands/intent.js.map +1 -1
- package/dist/commands/pain-evidence.d.ts +6 -0
- package/dist/commands/pain-evidence.d.ts.map +1 -1
- package/dist/commands/pain-evidence.js +6 -0
- package/dist/commands/pain-evidence.js.map +1 -1
- package/dist/commands/pain-record.d.ts.map +1 -1
- package/dist/commands/pain-record.js +21 -0
- package/dist/commands/pain-record.js.map +1 -1
- package/dist/commands/pain-retry.d.ts +4 -0
- package/dist/commands/pain-retry.d.ts.map +1 -1
- package/dist/commands/pain-retry.js +327 -32
- package/dist/commands/pain-retry.js.map +1 -1
- package/dist/commands/proven-channel-baseline.d.ts.map +1 -1
- package/dist/commands/proven-channel-baseline.js +4 -2
- package/dist/commands/proven-channel-baseline.js.map +1 -1
- package/dist/commands/runtime-activation.d.ts +30 -0
- package/dist/commands/runtime-activation.d.ts.map +1 -1
- package/dist/commands/runtime-activation.js +567 -11
- package/dist/commands/runtime-activation.js.map +1 -1
- package/dist/commands/runtime-canary.d.ts.map +1 -1
- package/dist/commands/runtime-canary.js +32 -7
- package/dist/commands/runtime-canary.js.map +1 -1
- package/dist/commands/runtime-health-snapshot.d.ts.map +1 -1
- package/dist/commands/runtime-health-snapshot.js +4 -2
- package/dist/commands/runtime-health-snapshot.js.map +1 -1
- package/dist/commands/runtime-init.d.ts +43 -0
- package/dist/commands/runtime-init.d.ts.map +1 -0
- package/dist/commands/runtime-init.js +264 -0
- package/dist/commands/runtime-init.js.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts +25 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.js +374 -0
- package/dist/commands/runtime-internalization-context-trace.js.map +1 -0
- package/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js +3 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
- package/dist/commands/runtime-internalization-integrity.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-integrity.js +4 -2
- package/dist/commands/runtime-internalization-integrity.js.map +1 -1
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +20 -5
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.d.ts +1 -0
- package/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.js +149 -6
- package/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/dist/commands/runtime-uat.d.ts.map +1 -1
- package/dist/commands/runtime-uat.guard.test.js +13 -15
- package/dist/commands/runtime-uat.guard.test.js.map +1 -1
- package/dist/commands/runtime-uat.js +4 -3
- package/dist/commands/runtime-uat.js.map +1 -1
- package/dist/config-reader.js +1 -1
- package/dist/config-reader.js.map +1 -1
- package/dist/index.js +54 -78
- package/dist/index.js.map +1 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +2 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +1 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js +304 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js.map +1 -0
- package/dist/services/__tests__/rulehost-readiness.test.js.map +1 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -1
- package/dist/services/config-doctor.d.ts.map +1 -1
- package/dist/services/config-doctor.js +9 -2
- package/dist/services/config-doctor.js.map +1 -1
- package/dist/services/pain-flood-simulation-runner.d.ts.map +1 -1
- package/dist/services/pain-flood-simulation-runner.js +9 -0
- package/dist/services/pain-flood-simulation-runner.js.map +1 -1
- package/dist/services/pd-config-loader.js +2 -2
- package/dist/services/pd-config-loader.js.map +1 -1
- package/dist/services/proven-channel-baseline-runner.d.ts.map +1 -1
- package/dist/services/proven-channel-baseline-runner.js +10 -2
- package/dist/services/proven-channel-baseline-runner.js.map +1 -1
- package/dist/services/quality-scorecard/data-extractor.js.map +1 -1
- package/dist/services/resolve-runtime-from-pd-config.js.map +1 -1
- package/dist/services/rulehost-pipeline-runner.d.ts +77 -1
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +111 -7
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/dist/services/rulehost-readiness.js.map +1 -1
- package/dist/services/runtime-adapter-resolver.d.ts +1 -0
- package/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
- package/dist/services/runtime-adapter-resolver.js +3 -0
- package/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/dist/services/synthetic-baseline-runner.d.ts.map +1 -1
- package/dist/services/synthetic-baseline-runner.js +11 -0
- package/dist/services/synthetic-baseline-runner.js.map +1 -1
- package/dist/utils/production-workspace-guard.d.ts +4 -4
- package/dist/utils/production-workspace-guard.d.ts.map +1 -1
- package/dist/utils/production-workspace-guard.js +19 -16
- package/dist/utils/production-workspace-guard.js.map +1 -1
- package/dist/utils/production-workspace-guard.test.js +54 -19
- package/dist/utils/production-workspace-guard.test.js.map +1 -1
- package/package.json +8 -5
- package/scripts/migrate-illegal-expected-decision.ts +252 -0
- package/src/commands/__tests__/intent-flag-wiring.test.ts +88 -3
- package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +73 -0
- package/src/commands/__tests__/rulecode-flag-wiring.test.ts +31 -24
- package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +12 -0
- package/src/commands/admission-gate.ts +30 -0
- package/src/commands/candidate.ts +74 -6
- package/src/commands/console.ts +40 -0
- package/src/commands/demo-story-a.ts +4 -2
- package/src/commands/diagnose.ts +158 -10
- package/src/commands/errors-list.ts +440 -0
- package/src/commands/health-codex.ts +249 -0
- package/src/commands/health.ts +60 -0
- package/src/commands/intent.ts +98 -29
- package/src/commands/pain-evidence.ts +6 -0
- package/src/commands/pain-record.ts +21 -0
- package/src/commands/pain-retry.ts +353 -35
- package/src/commands/proven-channel-baseline.ts +4 -2
- package/src/commands/runtime-activation.ts +657 -12
- package/src/commands/runtime-canary.ts +31 -5
- package/src/commands/runtime-health-snapshot.ts +4 -2
- package/src/commands/runtime-init.ts +305 -0
- package/src/commands/runtime-internalization-context-trace.ts +445 -0
- package/src/commands/runtime-internalization-enqueue-successors.ts +3 -1
- package/src/commands/runtime-internalization-integrity.ts +4 -2
- package/src/commands/runtime-internalization-run-once.ts +20 -5
- package/src/commands/runtime-internalization-run-rulehost.ts +159 -6
- package/src/commands/runtime-uat.guard.test.ts +14 -16
- package/src/commands/runtime-uat.ts +4 -3
- package/src/config-reader.ts +1 -1
- package/src/index.ts +69 -78
- package/src/services/__tests__/evaluator-runner-deps.test.ts +341 -0
- package/src/services/config-doctor.ts +9 -2
- package/src/services/pain-flood-simulation-runner.ts +9 -0
- package/src/services/pd-config-loader.ts +2 -2
- package/src/services/proven-channel-baseline-runner.ts +10 -3
- package/src/services/rulehost-pipeline-runner.ts +157 -6
- package/src/services/runtime-adapter-resolver.ts +4 -0
- package/src/services/synthetic-baseline-runner.ts +11 -0
- package/src/utils/production-workspace-guard.test.ts +62 -20
- package/src/utils/production-workspace-guard.ts +19 -16
- package/tests/bdd/cli-contract.steps.ts +343 -0
- package/tests/bdd/context-trace.steps.ts +229 -0
- package/tests/bdd/support/gherkin-loader.ts +147 -0
- package/tests/bdd/support/repo-root.ts +62 -0
- package/tests/bdd/support/vitest-bdd.ts +179 -0
- package/tests/commands/admission-gate-coverage.test.ts +79 -0
- package/tests/commands/candidate-audit-repair.test.ts +77 -0
- package/tests/commands/candidate-intake.test.ts +155 -10
- package/tests/commands/candidate-internalization-backfill.test.ts +30 -0
- package/tests/commands/cli-help-snapshot.test.ts +15 -1
- package/tests/commands/console-open.test.ts +19 -0
- package/tests/commands/diagnose.test.ts +619 -14
- package/tests/commands/errors-list.test.ts +678 -0
- package/tests/commands/health-codex.test.ts +243 -0
- package/tests/commands/health-host-wiring.test.ts +76 -0
- package/tests/commands/intent.test.ts +4 -4
- package/tests/commands/pain-record.test.ts +80 -0
- package/tests/commands/pain-retry.test.ts +94 -3
- package/tests/commands/run-rulehost-handler.test.ts +133 -0
- package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +227 -0
- package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +167 -0
- package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +262 -0
- package/tests/commands/runtime-activation-list-flag-wiring.test.ts +193 -0
- package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +222 -0
- package/tests/commands/runtime-activation.test.ts +1090 -5
- package/tests/commands/runtime-canary.test.ts +50 -0
- package/tests/commands/runtime-features.test.ts +2 -0
- package/tests/commands/runtime-init-empty-workspace.test.ts +234 -0
- package/tests/commands/runtime-init.test.ts +346 -0
- package/tests/commands/runtime-internalization-context-trace.test.ts +110 -0
- package/tests/commands/runtime-internalization.test.ts +2 -2
- package/tests/config-reader.test.ts +6 -5
- package/tests/e2e/cross-package-acceptance.test.ts +55 -13
- package/tests/services/mainline-snapshot-assembler.test.ts +6 -3
- package/tests/services/resolve-runtime-from-pd-config.test.ts +2 -0
- package/tests/services/rulehost-pipeline-runner.test.ts +1 -1
- package/vitest.config.ts +1 -1
package/src/commands/health.ts
CHANGED
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import Database from 'better-sqlite3';
|
|
14
|
+
import type { Command } from 'commander';
|
|
14
15
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
15
16
|
import { PruningReadModel, PainChainReadModel, auditCandidateLedgerConsistency } from '@principles/core/runtime-v2';
|
|
16
17
|
import type { PainChainTrace } from '@principles/core/runtime-v2';
|
|
17
18
|
import { getLedgerFilePathPublic } from '@principles/core/principle-tree-ledger';
|
|
19
|
+
import { handleHealthCodex } from './health-codex.js';
|
|
18
20
|
|
|
19
21
|
interface LastSuccessfulChain {
|
|
20
22
|
painId?: string;
|
|
@@ -188,3 +190,61 @@ export async function handleHealth(opts: HealthOptions = {}): Promise<void> {
|
|
|
188
190
|
|
|
189
191
|
writeHealth();
|
|
190
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Handlers backing the `pd health` command. Injected so the registration
|
|
196
|
+
* wiring test can exercise the real `registerHealthCommand` without executing
|
|
197
|
+
* real workspace/DB I/O; production uses the defaults.
|
|
198
|
+
*/
|
|
199
|
+
interface HealthCommandHandlers {
|
|
200
|
+
health?: typeof handleHealth;
|
|
201
|
+
healthCodex?: typeof handleHealthCodex;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Registers the `pd health` command on a Commander program.
|
|
206
|
+
*
|
|
207
|
+
* cli-7 test-wiring: this is the single registration used by both the CLI
|
|
208
|
+
* entrypoint and the wiring test, so a test exercising it covers the real
|
|
209
|
+
* production command (options + --host dispatch), not a copy.
|
|
210
|
+
*
|
|
211
|
+
* --host accepts only `openclaw` and `codex`; any other value is rejected with
|
|
212
|
+
* a structured reason/nextAction and a non-zero exit (cli-1/cli-2/cli-6). When
|
|
213
|
+
* `--json` is set, the rejection is emitted as exactly one parseable JSON
|
|
214
|
+
* object on stdout.
|
|
215
|
+
*/
|
|
216
|
+
export function registerHealthCommand(program: Command, handlers: HealthCommandHandlers = {}): void {
|
|
217
|
+
const healthHandler = handlers.health ?? handleHealth;
|
|
218
|
+
const codexHandler = handlers.healthCodex ?? handleHealthCodex;
|
|
219
|
+
program
|
|
220
|
+
.command('health')
|
|
221
|
+
.description('Show health diagnostics for all workspaces')
|
|
222
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
223
|
+
.option('--json', 'Output raw JSON')
|
|
224
|
+
.option('--host <host>', 'Host to inspect (openclaw|codex). Defaults to openclaw workspace health.')
|
|
225
|
+
.action(async (opts) => {
|
|
226
|
+
const host = opts.host ?? 'openclaw';
|
|
227
|
+
if (host !== 'codex' && host !== 'openclaw') {
|
|
228
|
+
// cli-2: exit-stops — return after setting exitCode.
|
|
229
|
+
if (opts.json) {
|
|
230
|
+
console.log(JSON.stringify({
|
|
231
|
+
ok: false,
|
|
232
|
+
reason: 'invalid_host',
|
|
233
|
+
host,
|
|
234
|
+
nextAction: `Use a supported host: openclaw or codex.`,
|
|
235
|
+
}));
|
|
236
|
+
process.exitCode = 1;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
console.error(`Invalid --host "${host}". Supported hosts: openclaw, codex.`);
|
|
240
|
+
console.error(`nextAction: run \`pd health --host openclaw\` or \`pd health --host codex\`.`);
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (host === 'codex') {
|
|
245
|
+
await codexHandler(opts);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
await healthHandler(opts);
|
|
249
|
+
});
|
|
250
|
+
}
|
package/src/commands/intent.ts
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
* pd intent — Owner-authored INTENT.md management (PRI-466).
|
|
3
3
|
*
|
|
4
4
|
* Subcommands:
|
|
5
|
-
* - init : create .principles/INTENT.md from the canonical template
|
|
6
|
-
* - show : display a read-only summary of INTENT.md (sections, hash, warnings)
|
|
5
|
+
* - init : create .principles/INTENT.{lang}.md from the canonical template
|
|
6
|
+
* - show : display a read-only summary of INTENT.{lang}.md (sections, hash, warnings)
|
|
7
7
|
*
|
|
8
8
|
* `init` is not gated by the intent_engineering flag — the Owner can
|
|
9
9
|
* initialise the intent doc at any time. `show` IS gated: flag-off returns
|
|
10
10
|
* a structured `flag_disabled` result without touching the filesystem,
|
|
11
11
|
* matching the Console backend contract.
|
|
12
12
|
*
|
|
13
|
+
* Bilingual: --lang zh-CN|en (default zh-CN) selects the intent doc language.
|
|
14
|
+
* File naming: INTENT.zh-CN.md / INTENT.en.md via getIntentFilename(lang).
|
|
15
|
+
*
|
|
13
16
|
* JSON mode is strict: --json outputs exactly one parseable JSON object on
|
|
14
17
|
* stdout (CLI Operator Gate rule 1). Failure paths include structured
|
|
15
18
|
* reason + nextAction (rule 6).
|
|
@@ -27,13 +30,14 @@ import * as path from 'node:path';
|
|
|
27
30
|
import type { Command } from 'commander';
|
|
28
31
|
import {
|
|
29
32
|
INTENT_MAX_BYTES,
|
|
30
|
-
|
|
33
|
+
getIntentFilename,
|
|
34
|
+
createIntentTemplate,
|
|
31
35
|
parseIntentDocSections,
|
|
32
36
|
computeIntentContentHash,
|
|
33
37
|
validateIntentDocSections,
|
|
34
38
|
isFeatureEnabled,
|
|
35
39
|
} from '@principles/core/runtime-v2';
|
|
36
|
-
import type { IntentDocSections, IntentDocWarning } from '@principles/core/runtime-v2';
|
|
40
|
+
import type { IntentDocSections, IntentDocWarning, IntentLang } from '@principles/core/runtime-v2';
|
|
37
41
|
import { resolveWorkspaceDir } from '../resolve-workspace.js';
|
|
38
42
|
import { loadPdConfig, computeFlagsFromLoadResult } from '../services/pd-config-loader.js';
|
|
39
43
|
import { emitResult } from '../services/cli-output.js';
|
|
@@ -41,7 +45,23 @@ import { emitResult } from '../services/cli-output.js';
|
|
|
41
45
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
42
46
|
|
|
43
47
|
const INTENT_DIR = '.principles';
|
|
44
|
-
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse and validate the --lang option.
|
|
51
|
+
*
|
|
52
|
+
* Returns:
|
|
53
|
+
* - 'zh-CN' | 'en' when value is undefined (default zh-CN) or a valid lang.
|
|
54
|
+
* - null when value is a non-empty string that is neither 'zh-CN' nor 'en'.
|
|
55
|
+
*
|
|
56
|
+
* rc-9: invalid values MUST NOT silently fall back to zh-CN — that would
|
|
57
|
+
* let a typo like `--lang zh` quietly write to INTENT.zh-CN.md. Callers
|
|
58
|
+
* emit a structured `invalid_lang` error when null is returned.
|
|
59
|
+
*/
|
|
60
|
+
export function parseLang(value: string | undefined): IntentLang | null {
|
|
61
|
+
if (value === undefined) return 'zh-CN';
|
|
62
|
+
if (value === 'zh-CN' || value === 'en') return value;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
45
65
|
|
|
46
66
|
// ── Output types ─────────────────────────────────────────────────────────────
|
|
47
67
|
|
|
@@ -54,7 +74,7 @@ export interface IntentInitOutput {
|
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
export interface IntentShowOutput {
|
|
57
|
-
status: 'ok' | 'flag_disabled' | 'not_found' | 'oversized' | 'read_error';
|
|
77
|
+
status: 'ok' | 'skipped' | 'flag_disabled' | 'not_found' | 'oversized' | 'read_error';
|
|
58
78
|
flagEnabled: boolean;
|
|
59
79
|
found: boolean;
|
|
60
80
|
path?: string;
|
|
@@ -68,8 +88,8 @@ export interface IntentShowOutput {
|
|
|
68
88
|
|
|
69
89
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
70
90
|
|
|
71
|
-
function getIntentFilePath(workspaceDir: string): string {
|
|
72
|
-
return path.join(workspaceDir, INTENT_DIR,
|
|
91
|
+
function getIntentFilePath(workspaceDir: string, lang: IntentLang): string {
|
|
92
|
+
return path.join(workspaceDir, INTENT_DIR, getIntentFilename(lang));
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
function sectionsToRecord(sections: IntentDocSections): Record<string, string> {
|
|
@@ -82,9 +102,9 @@ function sectionsToRecord(sections: IntentDocSections): Record<string, string> {
|
|
|
82
102
|
return record;
|
|
83
103
|
}
|
|
84
104
|
|
|
85
|
-
function formatIntentShowText(o: IntentShowOutput): string {
|
|
105
|
+
function formatIntentShowText(o: IntentShowOutput, filename: string): string {
|
|
86
106
|
const lines: string[] = [];
|
|
87
|
-
lines.push(
|
|
107
|
+
lines.push(`${filename} — ${o.path}`);
|
|
88
108
|
lines.push(`Content hash: ${o.contentHash}`);
|
|
89
109
|
lines.push(`Last edited: ${o.lastEditedAt}`);
|
|
90
110
|
lines.push('');
|
|
@@ -114,9 +134,29 @@ export interface IntentInitOptions {
|
|
|
114
134
|
json?: boolean;
|
|
115
135
|
dryRun?: boolean;
|
|
116
136
|
confirm?: boolean;
|
|
137
|
+
lang?: string;
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
141
|
+
const lang = parseLang(opts.lang);
|
|
142
|
+
|
|
143
|
+
// rc-9: invalid --lang must not silently fall back. Emit structured error.
|
|
144
|
+
if (lang === null) {
|
|
145
|
+
const output: IntentInitOutput = {
|
|
146
|
+
status: 'skipped',
|
|
147
|
+
path: '',
|
|
148
|
+
overwritten: false,
|
|
149
|
+
reason: 'invalid_lang',
|
|
150
|
+
nextAction: `--lang must be 'zh-CN' or 'en'. Received: ${opts.lang ?? ''}`,
|
|
151
|
+
};
|
|
152
|
+
emitResult(output, {
|
|
153
|
+
json: opts.json ?? false,
|
|
154
|
+
formatText: (o) => `Error: ${o.reason}\n→ ${o.nextAction}`,
|
|
155
|
+
});
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
120
160
|
// CLI Gate rule 4: --dry-run and --confirm must be mutually exclusive.
|
|
121
161
|
if (opts.dryRun && opts.confirm) {
|
|
122
162
|
const output: IntentInitOutput = {
|
|
@@ -141,7 +181,7 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
141
181
|
let dir: string;
|
|
142
182
|
try {
|
|
143
183
|
workspaceDir = resolveWorkspaceDir(opts.workspace);
|
|
144
|
-
filePath = getIntentFilePath(workspaceDir);
|
|
184
|
+
filePath = getIntentFilePath(workspaceDir, lang);
|
|
145
185
|
dir = path.dirname(filePath);
|
|
146
186
|
} catch (err) {
|
|
147
187
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -160,6 +200,8 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
160
200
|
return;
|
|
161
201
|
}
|
|
162
202
|
|
|
203
|
+
const filename = getIntentFilename(lang);
|
|
204
|
+
|
|
163
205
|
// CLI Gate rule 4: state-mutating command defaults to dry-run unless --confirm.
|
|
164
206
|
const isDryRun = opts.dryRun === true || opts.confirm !== true;
|
|
165
207
|
|
|
@@ -170,11 +212,11 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
170
212
|
path: filePath,
|
|
171
213
|
overwritten: false,
|
|
172
214
|
reason: 'file_exists',
|
|
173
|
-
nextAction: `Use --force to overwrite: pd intent init --force --confirm --workspace "${workspaceDir}"`,
|
|
215
|
+
nextAction: `Use --force to overwrite: pd intent init --force --confirm --lang ${lang} --workspace "${workspaceDir}"`,
|
|
174
216
|
};
|
|
175
217
|
emitResult(output, {
|
|
176
218
|
json: opts.json ?? false,
|
|
177
|
-
formatText: (o) =>
|
|
219
|
+
formatText: (o) => `${filename} already exists at ${o.path}\n→ ${o.nextAction}`,
|
|
178
220
|
});
|
|
179
221
|
process.exitCode = 1;
|
|
180
222
|
return;
|
|
@@ -186,17 +228,17 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
186
228
|
path: filePath,
|
|
187
229
|
overwritten: opts.force === true,
|
|
188
230
|
reason: 'dry_run',
|
|
189
|
-
nextAction: `Confirm write: pd intent init --confirm${opts.force ? ' --force' : ''} --workspace "${workspaceDir}"`,
|
|
231
|
+
nextAction: `Confirm write: pd intent init --confirm --lang ${lang}${opts.force ? ' --force' : ''} --workspace "${workspaceDir}"`,
|
|
190
232
|
};
|
|
191
233
|
emitResult(output, {
|
|
192
234
|
json: opts.json ?? false,
|
|
193
|
-
formatText: (o) => `[dry-run] Would create
|
|
235
|
+
formatText: (o) => `[dry-run] Would create ${filename} at ${o.path}${o.overwritten ? ' (overwritten)' : ''}\n→ ${o.nextAction}`,
|
|
194
236
|
});
|
|
195
237
|
return;
|
|
196
238
|
}
|
|
197
239
|
|
|
198
240
|
fs.mkdirSync(dir, { recursive: true });
|
|
199
|
-
fs.writeFileSync(filePath,
|
|
241
|
+
fs.writeFileSync(filePath, createIntentTemplate(lang), 'utf8');
|
|
200
242
|
|
|
201
243
|
const output: IntentInitOutput = {
|
|
202
244
|
status: 'ok',
|
|
@@ -205,7 +247,7 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
205
247
|
};
|
|
206
248
|
emitResult(output, {
|
|
207
249
|
json: opts.json ?? false,
|
|
208
|
-
formatText: (o) => `Created
|
|
250
|
+
formatText: (o) => `Created ${filename} at ${o.path}${o.overwritten ? ' (overwritten)' : ''}\nNext: edit the file to declare your project intent, then run "pd intent show --lang ${lang}".`,
|
|
209
251
|
});
|
|
210
252
|
} catch (err) {
|
|
211
253
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -228,9 +270,30 @@ export async function handleIntentInit(opts: IntentInitOptions): Promise<void> {
|
|
|
228
270
|
export interface IntentShowOptions {
|
|
229
271
|
workspace?: string;
|
|
230
272
|
json?: boolean;
|
|
273
|
+
lang?: string;
|
|
231
274
|
}
|
|
232
275
|
|
|
233
276
|
export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
277
|
+
const lang = parseLang(opts.lang);
|
|
278
|
+
|
|
279
|
+
// rc-9: invalid --lang must not silently fall back. Emit structured error.
|
|
280
|
+
if (lang === null) {
|
|
281
|
+
const output: IntentShowOutput = {
|
|
282
|
+
status: 'skipped',
|
|
283
|
+
flagEnabled: false,
|
|
284
|
+
found: false,
|
|
285
|
+
warnings: [],
|
|
286
|
+
reason: 'invalid_lang',
|
|
287
|
+
nextAction: `--lang must be 'zh-CN' or 'en'. Received: ${opts.lang ?? ''}`,
|
|
288
|
+
};
|
|
289
|
+
emitResult(output, {
|
|
290
|
+
json: opts.json ?? false,
|
|
291
|
+
formatText: (o) => `Error: ${o.reason}\n→ ${o.nextAction}`,
|
|
292
|
+
});
|
|
293
|
+
process.exitCode = 1;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
234
297
|
// CLI Gate rule 6: workspace resolution inside try/catch for structured errors.
|
|
235
298
|
let workspaceDir: string;
|
|
236
299
|
try {
|
|
@@ -258,6 +321,8 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
258
321
|
const flagsResult = computeFlagsFromLoadResult(configResult);
|
|
259
322
|
const flagEnabled = isFeatureEnabled(flagsResult, 'intent_engineering');
|
|
260
323
|
|
|
324
|
+
const filename = getIntentFilename(lang);
|
|
325
|
+
|
|
261
326
|
if (!flagEnabled) {
|
|
262
327
|
const output: IntentShowOutput = {
|
|
263
328
|
status: 'flag_disabled',
|
|
@@ -265,7 +330,7 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
265
330
|
found: false,
|
|
266
331
|
warnings: [],
|
|
267
332
|
reason: 'flag_disabled',
|
|
268
|
-
nextAction:
|
|
333
|
+
nextAction: `Enable the intent_engineering feature flag in .pd/config.yaml to read ${filename}.`,
|
|
269
334
|
};
|
|
270
335
|
emitResult(output, {
|
|
271
336
|
json: opts.json ?? false,
|
|
@@ -274,7 +339,7 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
274
339
|
return;
|
|
275
340
|
}
|
|
276
341
|
|
|
277
|
-
const filePath = getIntentFilePath(workspaceDir);
|
|
342
|
+
const filePath = getIntentFilePath(workspaceDir, lang);
|
|
278
343
|
|
|
279
344
|
try {
|
|
280
345
|
if (!fs.existsSync(filePath)) {
|
|
@@ -284,11 +349,11 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
284
349
|
found: false,
|
|
285
350
|
warnings: [],
|
|
286
351
|
reason: 'not_found',
|
|
287
|
-
nextAction: `Create
|
|
352
|
+
nextAction: `Create ${filename}: pd intent init --confirm --lang ${lang} --workspace "${workspaceDir}"`,
|
|
288
353
|
};
|
|
289
354
|
emitResult(output, {
|
|
290
355
|
json: opts.json ?? false,
|
|
291
|
-
formatText: (o) =>
|
|
356
|
+
formatText: (o) => `${filename} not found.\n→ ${o.nextAction}`,
|
|
292
357
|
});
|
|
293
358
|
return;
|
|
294
359
|
}
|
|
@@ -301,11 +366,11 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
301
366
|
found: true,
|
|
302
367
|
warnings: [],
|
|
303
368
|
reason: 'oversized',
|
|
304
|
-
nextAction:
|
|
369
|
+
nextAction: `${filename} exceeds ${INTENT_MAX_BYTES} bytes (${stat.size} bytes). Reduce content.`,
|
|
305
370
|
};
|
|
306
371
|
emitResult(output, {
|
|
307
372
|
json: opts.json ?? false,
|
|
308
|
-
formatText: (o) =>
|
|
373
|
+
formatText: (o) => `${filename} is too large.\n→ ${o.nextAction}`,
|
|
309
374
|
});
|
|
310
375
|
return;
|
|
311
376
|
}
|
|
@@ -327,7 +392,7 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
327
392
|
};
|
|
328
393
|
emitResult(output, {
|
|
329
394
|
json: opts.json ?? false,
|
|
330
|
-
formatText: (o) => formatIntentShowText(o),
|
|
395
|
+
formatText: (o) => formatIntentShowText(o, filename),
|
|
331
396
|
});
|
|
332
397
|
} catch (err) {
|
|
333
398
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -341,7 +406,7 @@ export async function handleIntentShow(opts: IntentShowOptions): Promise<void> {
|
|
|
341
406
|
};
|
|
342
407
|
emitResult(output, {
|
|
343
408
|
json: opts.json ?? false,
|
|
344
|
-
formatText: (o) => `Error reading
|
|
409
|
+
formatText: (o) => `Error reading ${filename}: ${o.reason}\n→ ${o.nextAction}`,
|
|
345
410
|
});
|
|
346
411
|
process.exitCode = 1;
|
|
347
412
|
}
|
|
@@ -356,11 +421,12 @@ export function registerIntentCommand(parentCmd: Command): Command {
|
|
|
356
421
|
|
|
357
422
|
intentCmd
|
|
358
423
|
.command('init')
|
|
359
|
-
.description('Create .principles/INTENT.md from the canonical template')
|
|
424
|
+
.description('Create .principles/INTENT.{lang}.md from the canonical template')
|
|
360
425
|
.option('-w, --workspace <path>', 'Workspace directory')
|
|
361
|
-
.option('--force', 'Overwrite existing INTENT
|
|
426
|
+
.option('--force', 'Overwrite existing INTENT file')
|
|
362
427
|
.option('--dry-run', 'Show what would happen without writing (default)')
|
|
363
|
-
.option('--confirm', 'Actually write the file (required to create INTENT
|
|
428
|
+
.option('--confirm', 'Actually write the file (required to create INTENT file)')
|
|
429
|
+
.option('--lang <lang>', 'Language: zh-CN or en (default: zh-CN)')
|
|
364
430
|
.option('--json', 'Output raw JSON')
|
|
365
431
|
.action(async (opts) => {
|
|
366
432
|
await handleIntentInit({
|
|
@@ -369,18 +435,21 @@ export function registerIntentCommand(parentCmd: Command): Command {
|
|
|
369
435
|
json: opts.json === true,
|
|
370
436
|
dryRun: opts.dryRun === true,
|
|
371
437
|
confirm: opts.confirm === true,
|
|
438
|
+
lang: opts.lang,
|
|
372
439
|
});
|
|
373
440
|
});
|
|
374
441
|
|
|
375
442
|
intentCmd
|
|
376
443
|
.command('show')
|
|
377
|
-
.description('Display a read-only summary of INTENT.md (sections, hash, warnings)')
|
|
444
|
+
.description('Display a read-only summary of INTENT.{lang}.md (sections, hash, warnings)')
|
|
378
445
|
.option('-w, --workspace <path>', 'Workspace directory')
|
|
446
|
+
.option('--lang <lang>', 'Language: zh-CN or en (default: zh-CN)')
|
|
379
447
|
.option('--json', 'Output raw JSON')
|
|
380
448
|
.action(async (opts) => {
|
|
381
449
|
await handleIntentShow({
|
|
382
450
|
workspace: opts.workspace,
|
|
383
451
|
json: opts.json === true,
|
|
452
|
+
lang: opts.lang,
|
|
384
453
|
});
|
|
385
454
|
});
|
|
386
455
|
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* pd pain evidence [--workspace <path>] [--limit N] [--json]
|
|
9
9
|
*
|
|
10
10
|
* Shows the most recent TRIGGER_DECISION log entries.
|
|
11
|
+
*
|
|
12
|
+
* Note: TRIGGER_DECISION entries are pain-signal admission gate decisions
|
|
13
|
+
* (Gate B / TriggerController). These are distinct from GFI (General
|
|
14
|
+
* Friction Index) friction events like GFI_SLICE_RESET, which track
|
|
15
|
+
* session-level tool-friction resets and are NOT pain signals. This command
|
|
16
|
+
* does not surface GFI events; use `pd runtime health gfi` for GFI state.
|
|
11
17
|
*/
|
|
12
18
|
|
|
13
19
|
import * as fs from 'fs';
|
|
@@ -32,6 +32,27 @@ export async function handlePainRecord(opts: RecordOptions): Promise<void> {
|
|
|
32
32
|
console.error('Error: --reason <text> is required');
|
|
33
33
|
console.error('Usage: pd pain record --reason <text> [--score N] [--source manual] [--workspace <path>] [--session <id>] [--json]');
|
|
34
34
|
process.exit(1);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Bug-P fix: enforce reason length limit to prevent oversized data entering the pipeline.
|
|
39
|
+
// Consistent with pain-flood-simulation.ts (MAX_REASON_LENGTH = 500) and synthetic-baseline.ts.
|
|
40
|
+
// CodeRabbit review fix: emit JSON on --json and stop execution after exit (cli-1, cli-2, cli-5).
|
|
41
|
+
const MAX_REASON_LENGTH = 500;
|
|
42
|
+
if (opts.reason.length > MAX_REASON_LENGTH) {
|
|
43
|
+
if (opts.json) {
|
|
44
|
+
console.log(JSON.stringify({
|
|
45
|
+
status: 'failed',
|
|
46
|
+
reason: 'reason_too_long',
|
|
47
|
+
message: `--reason must be at most ${MAX_REASON_LENGTH} characters (got ${opts.reason.length})`,
|
|
48
|
+
nextAction: 'Shorten the reason text or split it into multiple pain records.',
|
|
49
|
+
}, null, 2));
|
|
50
|
+
} else {
|
|
51
|
+
console.error(`Error: --reason must be at most ${MAX_REASON_LENGTH} characters (got ${opts.reason.length})`);
|
|
52
|
+
console.error('Next action: shorten the reason text or split into multiple pain records.');
|
|
53
|
+
}
|
|
54
|
+
process.exit(1);
|
|
55
|
+
return;
|
|
35
56
|
}
|
|
36
57
|
|
|
37
58
|
if (opts.score !== undefined && (isNaN(opts.score) || opts.score < 0 || opts.score > 100)) {
|