@unbrained/pm-cli 2026.9.12 → 2026.9.14
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/marketplace.json +2 -2
- package/CHANGELOG.md +42 -0
- package/dist/cli/command-namespaces.js +1 -1
- package/dist/cli/commander-usage.js +2 -2
- package/dist/cli/error-guidance.js +1 -1
- package/dist/cli/extension-command-help.d.ts +2 -2
- package/dist/cli/extension-command-help.js +2 -2
- package/dist/cli/main.js +2 -2
- package/dist/cli-bundle/bundle-manifest.json +146 -146
- package/dist/cli-bundle/chunks/chunk-33PER336.js +236 -0
- package/dist/cli-bundle/chunks/{chunk-3ZJDGTYC.js → chunk-5JIJAWIF.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-A2Q64TGR.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-YU75JRTW.js → chunk-B6W5LIXK.js} +43 -43
- package/dist/cli-bundle/chunks/{chunk-GKY655T7.js → chunk-BY3ID7QI.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-7KF6HCRC.js → chunk-D5AJQU7F.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-7H33WPDF.js → chunk-DBHQIS6Z.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-TAGDYARO.js → chunk-GQMILIC5.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-2JAY4LFI.js → chunk-NYE6EREL.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-67H4PKQ5.js → chunk-O7SRS7Y3.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-X6POOZKD.js → chunk-XEV7BHH3.js} +29 -29
- package/dist/cli-bundle/chunks/{chunk-I6VQUHK6.js → chunk-ZYRN2OG3.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-KEZPDR6X.js → register-list-query-B2U36YWL.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-HEXA3ZDM.js → register-mutation-GX42CQYR.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-TEZT65JF.js → register-operations-ZV34KX5E.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-IIL74WUG.js → register-setup-W4OS7IZW.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-LWJBBM6X.js → chunk-6WZBO4VQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-TTRB47KM.js → chunk-7BUJABHB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-2BQ4JSJQ.js → chunk-C5NQVXKE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-CMCSUANO.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-IURELABV.js → chunk-CPQQIVDL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-EIXGDI77.js → chunk-D4FQV6TY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-YNBACVQ2.js → chunk-ENJH7CBZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KK7KSXUP.js → chunk-FHQLQOUZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-PERRTPIY.js → chunk-FNLAQJES.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-GGDO6VBT.js +30 -0
- package/dist/cli-bundle/focused-chunks/{chunk-7EKXAUKF.js → chunk-KWP3QTN7.js} +53 -53
- package/dist/cli-bundle/focused-chunks/{chunk-SDQOOL34.js → chunk-LFGTCEHX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-5PSYV3AT.js → chunk-N23WTRCC.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-5CRND7UA.js → chunk-O4EROPO3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-UFSMYVZF.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-MFVABYEU.js → chunk-ZFNRD7EN.js} +2 -2
- package/dist/cli-bundle/main.js +20 -20
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +28 -28
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +42 -42
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +8 -8
- package/dist/core/output/output.js +3 -3
- package/dist/core/sentry/instrument.js +1 -1
- package/dist/core/telemetry/consent.d.ts +1 -1
- package/dist/core/telemetry/consent.js +2 -2
- package/dist/core/telemetry/policy.d.ts +10 -0
- package/dist/core/telemetry/policy.js +2 -0
- package/dist/core/telemetry/runtime.d.ts +43 -14
- package/dist/core/telemetry/runtime.js +5 -4
- package/dist/sdk/agent/command-suggestions.d.ts +6 -0
- package/dist/sdk/agent/command-suggestions.js +1 -1
- package/dist/sdk/agent/item-addressing.js +1 -1
- package/dist/sdk/agent-capability-contracts.js +1 -1
- package/dist/sdk/cli-bootstrap.js +1 -1
- package/dist/sdk/cli-contracts/command-aliases.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/grammar-contracts.d.ts +2 -2
- package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +7 -3
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
- package/dist/sdk/completion.js +1 -1
- package/dist/sdk/governance/health.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/telemetry.d.ts +5 -0
- package/dist/sdk/telemetry.js +1 -1
- package/docs/AGENT_UX_CONTRACTS.md +10 -2
- package/docs/CLI_GRAMMAR.md +59 -0
- package/docs/COMMAND_NAMESPACES.md +7 -4
- package/docs/CONFIGURATION.md +48 -3
- package/docs/generated/AGENT_CAPABILITY_ROUTING.md +4 -4
- package/docs/generated/AGENT_COMMAND_SURFACE.md +11 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +12 -1
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +2 -2
- package/marketplace.json +2 -2
- package/package.json +5 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/extensions/guide-shell/index.ts +13 -26
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/dist/cli-bundle/chunks/chunk-DEGRLB6Y.js +0 -235
- package/dist/cli-bundle/chunks/chunk-PTX2JRFR.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-BIOWOU6S.js +0 -30
- package/dist/cli-bundle/focused-chunks/chunk-GAKFECOQ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-VWMP6W7Y.js +0 -20
package/dist/sdk/telemetry.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GlobalOptions } from "../core/shared/command-types.js";
|
|
2
|
+
import { resolveTelemetryEnvironmentPolicy } from "../core/telemetry/policy.js";
|
|
2
3
|
/** Public contract for telemetry subcommands, shared by SDK and presentation-layer consumers. */
|
|
3
4
|
export declare const TELEMETRY_SUBCOMMANDS: readonly ["status", "flush", "stats", "clear"];
|
|
4
5
|
/** Restricts telemetry subcommand values accepted by command, SDK, and storage contracts. */
|
|
@@ -7,6 +8,10 @@ export type TelemetrySubcommand = (typeof TELEMETRY_SUBCOMMANDS)[number];
|
|
|
7
8
|
export interface TelemetryStatusSummary {
|
|
8
9
|
/** Whether telemetry delivery is enabled. */
|
|
9
10
|
enabled: boolean;
|
|
11
|
+
/** Persisted preference before process consent overrides. */
|
|
12
|
+
configured_enabled: boolean;
|
|
13
|
+
/** Process opt-outs, resolved without changing persisted preferences. */
|
|
14
|
+
env_overrides: ReturnType<typeof resolveTelemetryEnvironmentPolicy>;
|
|
10
15
|
/** Configured delivery endpoint. */
|
|
11
16
|
endpoint: string;
|
|
12
17
|
/** Local event queue path. */
|
package/dist/sdk/telemetry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="32a15ab4-82a2-546f-8519-d4e565e7b578")}catch(e2){}})();import fs from"node:fs/promises";import path from"node:path";import{pathExists,readFileIfExists}from"../core/fs/fs-utils.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{nowIso}from"../core/shared/time.js";import{resolveGlobalPmRoot}from"../core/store/paths.js";import{readSettings,writeSettings}from"../core/store/settings.js";import{flushTelemetryQueueNow}from"../core/telemetry/runtime.js";import{createUnknownSubcommandError}from"./agent/subcommand-recovery.js";const TELEMETRY_QUEUE_RELATIVE_PATH=path.join("runtime","telemetry","events.jsonl");const TELEMETRY_STATE_RELATIVE_PATH=path.join("runtime","telemetry","state.json");const TELEMETRY_RUNTIME_RELATIVE_PATH=path.join("runtime","telemetry");const DEFAULT_STATS_LIMIT=20;const TELEMETRY_SUBCOMMANDS=["status","flush","stats","clear"];const nearestRankPercentile=(sortedAscending,fraction)=>{const rank=Math.ceil(fraction*sortedAscending.length);const index=Math.min(sortedAscending.length-1,Math.max(0,rank-1));return sortedAscending[index]};const normalizeTelemetryText=(value,fallback)=>{if(typeof value!=="string"){return fallback}return value.trim().length>0?value:fallback};const normalizeFiniteInteger=value=>{if(typeof value!=="number"||!Number.isFinite(value)){return void 0}return Math.trunc(value)};const normalizeTelemetryQueueEntry=entry=>{return{command:normalizeTelemetryText(entry.event.command,"<unknown>"),eventType:normalizeTelemetryText(entry.event.event_type,"unknown"),eventSchemaVersion:normalizeFiniteInteger(entry.event.schema_version),clientSchemaVersion:normalizeFiniteInteger(entry.client_schema_version)}};const createTelemetryStatsAccumulator=()=>{return{count:0,max_attempts:0,event_type_counts:{},event_schema_versions:new Set,client_schema_versions:new Set,finish_durations_ms:[],ok_count:0,error_count:0,resolution_counts:{}}};const readTelemetryDuration=payload=>{const value=payload?.duration_ms;return typeof value==="number"&&Number.isFinite(value)?value:void 0};const readTelemetryResolution=payload=>{const value=payload?.command_resolution;return normalizeTelemetryText(value,"")||void 0};const incrementTelemetryCount=(counts,key)=>{counts[key]=(counts[key]??0)+1};const recordTelemetryFinishPayload=(current,payload)=>{const durationMs=readTelemetryDuration(payload);if(durationMs!==void 0){current.finish_durations_ms.push(durationMs)}if(payload?.ok===true){current.ok_count+=1}else{current.error_count+=1}const resolution=readTelemetryResolution(payload);if(resolution!==void 0){incrementTelemetryCount(current.resolution_counts,resolution)}};const normalizeTelemetryAttempts=attempts=>Number.isFinite(attempts)?Math.max(0,Math.trunc(attempts)):0;const addTelemetryDimension=(values,value)=>{if(value!==void 0){values.add(value)}};const recordTelemetryQueueEntry=(grouped,entry)=>{const normalized=normalizeTelemetryQueueEntry(entry);const current=grouped.get(normalized.command)??createTelemetryStatsAccumulator();current.count+=1;current.max_attempts=Math.max(current.max_attempts,normalizeTelemetryAttempts(entry.attempts));incrementTelemetryCount(current.event_type_counts,normalized.eventType);addTelemetryDimension(current.event_schema_versions,normalized.eventSchemaVersion);addTelemetryDimension(current.client_schema_versions,normalized.clientSchemaVersion);if(normalized.eventType==="command_finish"){recordTelemetryFinishPayload(current,entry.event.payload)}grouped.set(normalized.command,current)};const buildTelemetryStatsBucket=(command,value)=>{const finishCount=value.ok_count+value.error_count;const sortedDurations=[...value.finish_durations_ms].sort((left,right)=>left-right);const resolutionEntries=Object.entries(value.resolution_counts).sort((left,right)=>left[0].localeCompare(right[0]));return{command,count:value.count,event_type_counts:Object.fromEntries(Object.entries(value.event_type_counts).sort((left,right)=>left[0].localeCompare(right[0]))),max_attempts:value.max_attempts,event_schema_versions:[...value.event_schema_versions].sort((left,right)=>left-right),client_schema_versions:[...value.client_schema_versions].sort((left,right)=>left-right),...sortedDurations.length>0?{duration_p50_ms:nearestRankPercentile(sortedDurations,.5),duration_p95_ms:nearestRankPercentile(sortedDurations,.95),duration_max_ms:sortedDurations[sortedDurations.length-1]}:{},...finishCount>0?{ok_count:value.ok_count,error_count:value.error_count,error_rate:value.error_count/finishCount}:{},...resolutionEntries.length>0?{command_resolution_counts:Object.fromEntries(resolutionEntries)}:{}}};const normalizeTelemetrySubcommand=value=>{const normalized=(value??"status").trim().toLowerCase();if(TELEMETRY_SUBCOMMANDS.includes(normalized)){return normalized}throw createUnknownSubcommandError({command_path:"telemetry",token:value?.trim()||"<empty>",allowed:TELEMETRY_SUBCOMMANDS,examples:["pm telemetry status","pm telemetry flush","pm telemetry stats --limit 10","pm telemetry clear"]})};const throwInvalidTelemetryLimit=()=>{throw new PmCliError("--limit must be a positive integer",EXIT_CODE.USAGE)};const requirePositiveTelemetryLimit=value=>{if([!Number.isFinite(value),!Number.isInteger(value),value<=0].includes(true)){return throwInvalidTelemetryLimit()}return value};const parseTelemetryStringLimit=raw=>{const trimmed=raw.trim();if([trimmed.length===0,!/^\d+$/.test(trimmed)].includes(true)){return throwInvalidTelemetryLimit()}return requirePositiveTelemetryLimit(Number.parseInt(trimmed,10))};const parseTelemetryStatsLimit=raw=>{if(raw===void 0){return DEFAULT_STATS_LIMIT}if(typeof raw==="number"){return requirePositiveTelemetryLimit(raw)}if(typeof raw==="string"){return parseTelemetryStringLimit(raw)}return throwInvalidTelemetryLimit()};const isTelemetryQueueEntry=value=>{if(typeof value!=="object"||value===null){return false}const candidate=value;return typeof candidate.attempts==="number"&&typeof candidate.event==="object"&&candidate.event!==null};const parseTelemetryQueueRow=row=>{try{const parsed=JSON.parse(row);return isTelemetryQueueEntry(parsed)?parsed:void 0}catch{return void 0}};const splitTelemetryQueueRows=raw=>{if(raw===null){return[]}return raw.split("\n").map(line=>line.trim()).filter(line=>line.length>0)};const parseTelemetryQueue=raw=>{const rows=splitTelemetryQueueRows(raw);const entries=[];let invalidRows=0;for(const row of rows){const parsed=parseTelemetryQueueRow(row);if(parsed===void 0){invalidRows+=1}else{entries.push(parsed)}}return{rows_total:rows.length,valid_entries:entries.length,invalid_rows:invalidRows,entries}};const isTelemetryRuntimeState=value=>typeof value==="object"&&value!==null&&!Array.isArray(value);const parseTelemetryRuntimeState=raw=>{try{const parsed=JSON.parse(raw);return isTelemetryRuntimeState(parsed)?parsed:{}}catch{return{}}};const readTelemetryRuntimeState=async statePath=>{const stateRaw=await readFileIfExists(statePath);if(stateRaw===null||stateRaw.trim().length===0){return{}}return parseTelemetryRuntimeState(stateRaw)};const nullableTelemetryStateValue=value=>value??null;const buildTelemetryStatusSummary=async globalPmRoot=>{const settings=await readSettings(globalPmRoot);const queuePath=path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH);const statePath=path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH);const queueRaw=await readFileIfExists(queuePath);const queue=parseTelemetryQueue(queueRaw);const runtimeState=await readTelemetryRuntimeState(statePath);return{enabled:settings.telemetry.enabled,endpoint:settings.telemetry.endpoint,queue_path:queuePath,state_path:statePath,queue_rows_total:queue.rows_total,queue_entries:queue.valid_entries,queue_invalid_rows:queue.invalid_rows,queue_size_bytes:Buffer.byteLength(queueRaw??"","utf8"),last_attempted_flush_at:nullableTelemetryStateValue(runtimeState.last_attempted_flush_at),last_successful_flush_at:nullableTelemetryStateValue(runtimeState.last_successful_flush_at),last_failed_flush_at:nullableTelemetryStateValue(runtimeState.last_failed_flush_at),last_failed_flush_error:nullableTelemetryStateValue(runtimeState.last_failed_flush_error)}};const buildTelemetryStatsBuckets=entries=>{const grouped=new Map;for(const entry of entries){recordTelemetryQueueEntry(grouped,entry)}return[...grouped.entries()].map(([command,value])=>buildTelemetryStatsBucket(command,value)).sort((left,right)=>right.count-left.count||left.command.localeCompare(right.command))};const runTelemetryStatus=async(_options,context)=>({action:"telemetry",subcommand:"status",status:await buildTelemetryStatusSummary(context.globalPmRoot),generated_at:nowIso()});const runTelemetryFlush=async(_options,context)=>{const before=await buildTelemetryStatusSummary(context.globalPmRoot);await flushTelemetryQueueNow(context.globalPmRoot);const after=await buildTelemetryStatusSummary(context.globalPmRoot);const queueEntriesDrained=Math.max(0,before.queue_entries-after.queue_entries);const queueEmpty=after.queue_entries===0;return{action:"telemetry",subcommand:"flush",queue_entries_before:before.queue_entries,queue_entries_after:after.queue_entries,queue_entries_drained:queueEntriesDrained,queue_progressed:queueEntriesDrained>0,queue_empty:queueEmpty,queue_drained:queueEmpty,status:after,generated_at:nowIso()}};const runTelemetryStats=async(options,context)=>{const limit=parseTelemetryStatsLimit(options.limit);const queueRaw=await readFileIfExists(context.queuePath);const queue=parseTelemetryQueue(queueRaw);const buckets=buildTelemetryStatsBuckets(queue.entries);const selected=buckets.slice(0,limit);return{action:"telemetry",subcommand:"stats",limit,total_commands:buckets.length,queue_entries:queue.valid_entries,queue_invalid_rows:queue.invalid_rows,queue_rows_total:queue.rows_total,truncated:buckets.length>selected.length,stats:selected,generated_at:nowIso()}};const runTelemetryClear=async(_options,context)=>{const settings=await readSettings(context.globalPmRoot);const settingsChanged=[settings.telemetry.enabled!==false,settings.telemetry.installation_id!=="",settings.telemetry.first_run_prompt_completed!==true].includes(true);settings.telemetry.enabled=false;settings.telemetry.first_run_prompt_completed=true;settings.telemetry.installation_id="";if(settingsChanged){await writeSettings(context.globalPmRoot,settings,"telemetry:clear")}const telemetryRuntimePath=path.join(context.globalPmRoot,TELEMETRY_RUNTIME_RELATIVE_PATH);const existed=await pathExists(telemetryRuntimePath);await fs.rm(telemetryRuntimePath,{recursive:true,force:true});return{action:"telemetry",subcommand:"clear",settings_changed:settingsChanged,runtime_dir_removed:existed&&!await pathExists(telemetryRuntimePath),queue_exists_after:await pathExists(context.queuePath),state_exists_after:await pathExists(context.statePath),status:await buildTelemetryStatusSummary(context.globalPmRoot),generated_at:nowIso()}};const TELEMETRY_COMMAND_HANDLERS={status:runTelemetryStatus,flush:runTelemetryFlush,stats:runTelemetryStats,clear:runTelemetryClear};async function runTelemetry(options,_global){void _global;const subcommand=normalizeTelemetrySubcommand(options.subcommand);const globalPmRoot=resolveGlobalPmRoot(process.cwd());return TELEMETRY_COMMAND_HANDLERS[subcommand](options,{globalPmRoot,queuePath:path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH),statePath:path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH)})}export{TELEMETRY_SUBCOMMANDS,runTelemetry};
|
|
1
|
+
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0c2960a8-84b8-57d1-bf5f-cea9cde0f544")}catch(e2){}})();import fs from"node:fs/promises";import path from"node:path";import{pathExists,readFileIfExists}from"../core/fs/fs-utils.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{nowIso}from"../core/shared/time.js";import{resolveGlobalPmRoot}from"../core/store/paths.js";import{readSettings,writeSettings}from"../core/store/settings.js";import{resolveTelemetryEnvironmentPolicy}from"../core/telemetry/policy.js";import{flushTelemetryQueueNow,withTelemetryQueueMutation}from"../core/telemetry/runtime.js";import{createUnknownSubcommandError}from"./agent/subcommand-recovery.js";const TELEMETRY_QUEUE_RELATIVE_PATH=path.join("runtime","telemetry","events.jsonl");const TELEMETRY_STATE_RELATIVE_PATH=path.join("runtime","telemetry","state.json");const TELEMETRY_RUNTIME_RELATIVE_PATH=path.join("runtime","telemetry");const DEFAULT_STATS_LIMIT=20;const TELEMETRY_SUBCOMMANDS=["status","flush","stats","clear"];const nearestRankPercentile=(sortedAscending,fraction)=>{const rank=Math.ceil(fraction*sortedAscending.length);const index=Math.min(sortedAscending.length-1,Math.max(0,rank-1));return sortedAscending[index]};const normalizeTelemetryText=(value,fallback)=>{if(typeof value!=="string"){return fallback}return value.trim().length>0?value:fallback};const normalizeFiniteInteger=value=>{if(typeof value!=="number"||!Number.isFinite(value)){return void 0}return Math.trunc(value)};const normalizeTelemetryQueueEntry=entry=>{return{command:normalizeTelemetryText(entry.event.command,"<unknown>"),eventType:normalizeTelemetryText(entry.event.event_type,"unknown"),eventSchemaVersion:normalizeFiniteInteger(entry.event.schema_version),clientSchemaVersion:normalizeFiniteInteger(entry.client_schema_version)}};const createTelemetryStatsAccumulator=()=>{return{count:0,max_attempts:0,event_type_counts:{},event_schema_versions:new Set,client_schema_versions:new Set,finish_durations_ms:[],ok_count:0,error_count:0,resolution_counts:{}}};const readTelemetryDuration=payload=>{const value=payload?.duration_ms;return typeof value==="number"&&Number.isFinite(value)?value:void 0};const readTelemetryResolution=payload=>{const value=payload?.command_resolution;return normalizeTelemetryText(value,"")||void 0};const incrementTelemetryCount=(counts,key)=>{counts[key]=(counts[key]??0)+1};const recordTelemetryFinishPayload=(current,payload)=>{const durationMs=readTelemetryDuration(payload);if(durationMs!==void 0){current.finish_durations_ms.push(durationMs)}if(payload?.ok===true){current.ok_count+=1}else{current.error_count+=1}const resolution=readTelemetryResolution(payload);if(resolution!==void 0){incrementTelemetryCount(current.resolution_counts,resolution)}};const normalizeTelemetryAttempts=attempts=>Number.isFinite(attempts)?Math.max(0,Math.trunc(attempts)):0;const addTelemetryDimension=(values,value)=>{if(value!==void 0){values.add(value)}};const recordTelemetryQueueEntry=(grouped,entry)=>{const normalized=normalizeTelemetryQueueEntry(entry);const current=grouped.get(normalized.command)??createTelemetryStatsAccumulator();current.count+=1;current.max_attempts=Math.max(current.max_attempts,normalizeTelemetryAttempts(entry.attempts));incrementTelemetryCount(current.event_type_counts,normalized.eventType);addTelemetryDimension(current.event_schema_versions,normalized.eventSchemaVersion);addTelemetryDimension(current.client_schema_versions,normalized.clientSchemaVersion);if(normalized.eventType==="command_finish"){recordTelemetryFinishPayload(current,entry.event.payload)}grouped.set(normalized.command,current)};const buildTelemetryStatsBucket=(command,value)=>{const finishCount=value.ok_count+value.error_count;const sortedDurations=[...value.finish_durations_ms].sort((left,right)=>left-right);const resolutionEntries=Object.entries(value.resolution_counts).sort((left,right)=>left[0].localeCompare(right[0]));return{command,count:value.count,event_type_counts:Object.fromEntries(Object.entries(value.event_type_counts).sort((left,right)=>left[0].localeCompare(right[0]))),max_attempts:value.max_attempts,event_schema_versions:[...value.event_schema_versions].sort((left,right)=>left-right),client_schema_versions:[...value.client_schema_versions].sort((left,right)=>left-right),...sortedDurations.length>0?{duration_p50_ms:nearestRankPercentile(sortedDurations,.5),duration_p95_ms:nearestRankPercentile(sortedDurations,.95),duration_max_ms:sortedDurations[sortedDurations.length-1]}:{},...finishCount>0?{ok_count:value.ok_count,error_count:value.error_count,error_rate:value.error_count/finishCount}:{},...resolutionEntries.length>0?{command_resolution_counts:Object.fromEntries(resolutionEntries)}:{}}};const normalizeTelemetrySubcommand=value=>{const normalized=(value??"status").trim().toLowerCase();if(TELEMETRY_SUBCOMMANDS.includes(normalized)){return normalized}throw createUnknownSubcommandError({command_path:"telemetry",token:value?.trim()||"<empty>",allowed:TELEMETRY_SUBCOMMANDS,examples:["pm telemetry status","pm telemetry flush","pm telemetry stats --limit 10","pm telemetry clear"]})};const throwInvalidTelemetryLimit=()=>{throw new PmCliError("--limit must be a positive integer",EXIT_CODE.USAGE)};const requirePositiveTelemetryLimit=value=>{if([!Number.isFinite(value),!Number.isInteger(value),value<=0].includes(true)){return throwInvalidTelemetryLimit()}return value};const parseTelemetryStringLimit=raw=>{const trimmed=raw.trim();if([trimmed.length===0,!/^\d+$/.test(trimmed)].includes(true)){return throwInvalidTelemetryLimit()}return requirePositiveTelemetryLimit(Number.parseInt(trimmed,10))};const parseTelemetryStatsLimit=raw=>{if(raw===void 0){return DEFAULT_STATS_LIMIT}if(typeof raw==="number"){return requirePositiveTelemetryLimit(raw)}if(typeof raw==="string"){return parseTelemetryStringLimit(raw)}return throwInvalidTelemetryLimit()};const isTelemetryQueueEntry=value=>{if(typeof value!=="object"||value===null){return false}const candidate=value;return typeof candidate.attempts==="number"&&typeof candidate.event==="object"&&candidate.event!==null};const parseTelemetryQueueRow=row=>{try{const parsed=JSON.parse(row);return isTelemetryQueueEntry(parsed)?parsed:void 0}catch{return void 0}};const splitTelemetryQueueRows=raw=>{if(raw===null){return[]}return raw.split("\n").map(line=>line.trim()).filter(line=>line.length>0)};const parseTelemetryQueue=raw=>{const rows=splitTelemetryQueueRows(raw);const entries=[];let invalidRows=0;for(const row of rows){const parsed=parseTelemetryQueueRow(row);if(parsed===void 0){invalidRows+=1}else{entries.push(parsed)}}return{rows_total:rows.length,valid_entries:entries.length,invalid_rows:invalidRows,entries}};const isTelemetryRuntimeState=value=>typeof value==="object"&&value!==null&&!Array.isArray(value);const parseTelemetryRuntimeState=raw=>{try{const parsed=JSON.parse(raw);return isTelemetryRuntimeState(parsed)?parsed:{}}catch{return{}}};const readTelemetryRuntimeState=async statePath=>{const stateRaw=await readFileIfExists(statePath);if(stateRaw===null||stateRaw.trim().length===0){return{}}return parseTelemetryRuntimeState(stateRaw)};const nullableTelemetryStateValue=value=>value??null;const buildTelemetryStatusSummary=async globalPmRoot=>{const settings=await readSettings(globalPmRoot);const queuePath=path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH);const statePath=path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH);const queueRaw=await readFileIfExists(queuePath);const queue=parseTelemetryQueue(queueRaw);const runtimeState=await readTelemetryRuntimeState(statePath);const environmentPolicy=resolveTelemetryEnvironmentPolicy();return{enabled:settings.telemetry.enabled&&!environmentPolicy.telemetry_disabled,configured_enabled:settings.telemetry.enabled,env_overrides:environmentPolicy,endpoint:settings.telemetry.endpoint,queue_path:queuePath,state_path:statePath,queue_rows_total:queue.rows_total,queue_entries:queue.valid_entries,queue_invalid_rows:queue.invalid_rows,queue_size_bytes:Buffer.byteLength(queueRaw??"","utf8"),last_attempted_flush_at:nullableTelemetryStateValue(runtimeState.last_attempted_flush_at),last_successful_flush_at:nullableTelemetryStateValue(runtimeState.last_successful_flush_at),last_failed_flush_at:nullableTelemetryStateValue(runtimeState.last_failed_flush_at),last_failed_flush_error:nullableTelemetryStateValue(runtimeState.last_failed_flush_error)}};const buildTelemetryStatsBuckets=entries=>{const grouped=new Map;for(const entry of entries){recordTelemetryQueueEntry(grouped,entry)}return[...grouped.entries()].map(([command,value])=>buildTelemetryStatsBucket(command,value)).sort((left,right)=>right.count-left.count||left.command.localeCompare(right.command))};const runTelemetryStatus=async(_options,context)=>({action:"telemetry",subcommand:"status",status:await buildTelemetryStatusSummary(context.globalPmRoot),generated_at:nowIso()});const runTelemetryFlush=async(_options,context)=>{const before=await buildTelemetryStatusSummary(context.globalPmRoot);await flushTelemetryQueueNow(context.globalPmRoot);const after=await buildTelemetryStatusSummary(context.globalPmRoot);const queueEntriesDrained=Math.max(0,before.queue_entries-after.queue_entries);const queueEmpty=after.queue_entries===0;return{action:"telemetry",subcommand:"flush",queue_entries_before:before.queue_entries,queue_entries_after:after.queue_entries,queue_entries_drained:queueEntriesDrained,queue_progressed:queueEntriesDrained>0,queue_empty:queueEmpty,queue_drained:queueEmpty,status:after,generated_at:nowIso()}};const runTelemetryStats=async(options,context)=>{const limit=parseTelemetryStatsLimit(options.limit);const queueRaw=await readFileIfExists(context.queuePath);const queue=parseTelemetryQueue(queueRaw);const buckets=buildTelemetryStatsBuckets(queue.entries);const selected=buckets.slice(0,limit);return{action:"telemetry",subcommand:"stats",limit,total_commands:buckets.length,queue_entries:queue.valid_entries,queue_invalid_rows:queue.invalid_rows,queue_rows_total:queue.rows_total,truncated:buckets.length>selected.length,stats:selected,generated_at:nowIso()}};const runTelemetryClear=async(_options,context)=>{return withTelemetryQueueMutation(async()=>{const settings=await readSettings(context.globalPmRoot);const settingsChanged=[settings.telemetry.enabled!==false,settings.telemetry.installation_id!=="",settings.telemetry.first_run_prompt_completed!==true].includes(true);settings.telemetry.enabled=false;settings.telemetry.first_run_prompt_completed=true;settings.telemetry.installation_id="";if(settingsChanged){await writeSettings(context.globalPmRoot,settings,"telemetry:clear")}const telemetryRuntimePath=path.join(context.globalPmRoot,TELEMETRY_RUNTIME_RELATIVE_PATH);const existed=await pathExists(telemetryRuntimePath);await fs.rm(telemetryRuntimePath,{recursive:true,force:true});return{action:"telemetry",subcommand:"clear",settings_changed:settingsChanged,runtime_dir_removed:existed&&!await pathExists(telemetryRuntimePath),queue_exists_after:await pathExists(context.queuePath),state_exists_after:await pathExists(context.statePath),status:await buildTelemetryStatusSummary(context.globalPmRoot),generated_at:nowIso()}},context.globalPmRoot)};const TELEMETRY_COMMAND_HANDLERS={status:runTelemetryStatus,flush:runTelemetryFlush,stats:runTelemetryStats,clear:runTelemetryClear};async function runTelemetry(options,_global){void _global;const subcommand=normalizeTelemetrySubcommand(options.subcommand);const globalPmRoot=resolveGlobalPmRoot(process.cwd());return TELEMETRY_COMMAND_HANDLERS[subcommand](options,{globalPmRoot,queuePath:path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH),statePath:path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH)})}export{TELEMETRY_SUBCOMMANDS,runTelemetry};
|
|
2
2
|
//# sourceMappingURL=telemetry.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent UX Contracts
|
|
2
2
|
|
|
3
|
-
Tracker references: [pm-v1yo](../.agents/pm/issues/pm-v1yo.toon), [pm-i6pi](../.agents/pm/issues/pm-i6pi.toon), [pm-um4g](../.agents/pm/issues/pm-um4g.toon), [pm-tmhs](../.agents/pm/issues/pm-tmhs.toon), [pm-6m1i](../.agents/pm/issues/pm-6m1i.toon), [pm-cj9v](../.agents/pm/issues/pm-cj9v.toon), [pm-yp56](../.agents/pm/issues/pm-yp56.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon), [pm-rggtvd](../.agents/pm/issues/pm-rggtvd.toon), and [pm-vk7zek](../.agents/pm/issues/pm-vk7zek.toon).
|
|
3
|
+
Tracker references: [pm-bwkmp4](../.agents/pm/issues/pm-bwkmp4.toon), [pm-zsic8h](../.agents/pm/issues/pm-zsic8h.toon), [pm-4f86c4](../.agents/pm/issues/pm-4f86c4.toon), [pm-v1yo](../.agents/pm/issues/pm-v1yo.toon), [pm-i6pi](../.agents/pm/issues/pm-i6pi.toon), [pm-um4g](../.agents/pm/issues/pm-um4g.toon), [pm-tmhs](../.agents/pm/issues/pm-tmhs.toon), [pm-6m1i](../.agents/pm/issues/pm-6m1i.toon), [pm-cj9v](../.agents/pm/issues/pm-cj9v.toon), [pm-yp56](../.agents/pm/issues/pm-yp56.toon), [pm-gos426](../.agents/pm/issues/pm-gos426.toon), [pm-flnefm](../.agents/pm/issues/pm-flnefm.toon), [pm-rggtvd](../.agents/pm/issues/pm-rggtvd.toon), and [pm-vk7zek](../.agents/pm/issues/pm-vk7zek.toon).
|
|
4
4
|
|
|
5
5
|
These contracts keep common agent loops deterministic, token-efficient, and recoverable. Runtime contracts and `--help --json` remain the exact source for available flags.
|
|
6
6
|
|
|
@@ -50,7 +50,15 @@ Compatibility note: before the 2026.7.19 release, `findings_by_code` incorrectly
|
|
|
50
50
|
|
|
51
51
|
## Extension command ownership
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Extensions may add unique leaves beneath core groups: `ops metrics` can coexist with core `ops health`. Core leaf paths, including declared leaves awaiting lazy registration, remain reserved. A same-path handler collision emits `extension_command_collision:` with both owners and preserves core execution. Metadata-only registrations can augment an already registered core command; matching a core action name does not grant ownership of its path. Declared package-backed relocated facets retain their existing registration contract. Packages should rename a conflicting leaf instead of abandoning the shared namespace.
|
|
54
|
+
|
|
55
|
+
Existing `pm-ops` installations keep their `ops` namespace and command paths. Upgrading to the leaf-composition implementation restores commands such as `ops metrics` without reinstalling or renaming the package. For a true collision such as `ops health`, the warning identifies both owners; rename that extension leaf while keeping its non-conflicting siblings.
|
|
56
|
+
|
|
57
|
+
## Command recovery and compact reads
|
|
58
|
+
|
|
59
|
+
Unknown-command suggestions resolve deprecated spellings to their available canonical replacement, including required flags. For example, `pm start` suggests `pm claim --start`, and a misspelled `list-open` points to `pm list --status open`. The SDK exports `canonicalizeCommandSuggestions` alongside its ranking primitives so package hosts can apply the same alias policy. Its optional query token prefers available semantic matches, so `start` does not also suggest unrelated statistics commands. Unavailable replacements are omitted.
|
|
60
|
+
|
|
61
|
+
Default TOON lists with complete brief rows show the rows, count, and `details: "--full"` recovery pointer. Active filters, warnings, and additional diagnostics remain visible. A producer-owned `details` field preserves the detailed envelope instead of being replaced. Partial or truncated reads retain their detailed completeness and continuation receipts. `--full` restores item metadata; JSON retains the complete structured list envelope. Human recovery bundles echo an attempted command once; JSON retains the separate normalized argument vector for programmatic recovery.
|
|
54
62
|
|
|
55
63
|
## Context and work selection
|
|
56
64
|
|
package/docs/CLI_GRAMMAR.md
CHANGED
|
@@ -133,3 +133,62 @@ shared compatibility contracts carry the parser and discovery mapping.
|
|
|
133
133
|
Existing aliases retain their flags, arguments, and results and are hidden from
|
|
134
134
|
default discovery. `search -- advanced` preserves the literal query that would
|
|
135
135
|
otherwise collide with the package facet; options belong before the separator.
|
|
136
|
+
|
|
137
|
+
## Item evidence facets and internal routing
|
|
138
|
+
|
|
139
|
+
Tracked by [pm-yql1](../.agents/pm/tasks/pm-yql1.toon),
|
|
140
|
+
[pm-lp4j](../.agents/pm/tasks/pm-lp4j.toon), and
|
|
141
|
+
[pm-szdc](../.agents/pm/tasks/pm-szdc.toon).
|
|
142
|
+
|
|
143
|
+
`item comments`, `item notes`, `item learnings`, `item files`, `item docs`,
|
|
144
|
+
`item deps`, `item append`, and `item test` share the item noun. Each retains
|
|
145
|
+
its existing arguments, flags, output, and SDK operation identity. For example:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pm item comments pm-a1b2 --add "Verified the acceptance criteria."
|
|
149
|
+
pm item files pm-a1b2 --add path=src/example.ts,scope=project
|
|
150
|
+
pm item test pm-a1b2 --add 'command=node --test,scope=project'
|
|
151
|
+
pm item test pm-a1b2 --run --progress
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The old root spellings remain executable compatibility aliases. Singular
|
|
155
|
+
`comment`, `note`, `learning`, and plural `tests` resolve directly to the same
|
|
156
|
+
canonical leaves. SDK and MCP clients continue using their published operation
|
|
157
|
+
names; command relocation does not rename those APIs.
|
|
158
|
+
|
|
159
|
+
`item test worker` is internal orchestration plumbing. With `guide-shell`
|
|
160
|
+
installed, `completion statuses`, `completion tags`, and `completion types`
|
|
161
|
+
provide runtime shell values. These internal leaves remain directly invocable
|
|
162
|
+
but are excluded from public help and completion suggestions, including beneath
|
|
163
|
+
public parents. Existing worker and completion-helper spellings remain valid.
|
|
164
|
+
Default contract discovery also omits internal paths; use `contracts --full` or
|
|
165
|
+
an exact `contracts --command "item test worker" --flags-only` selection to
|
|
166
|
+
inspect their contracts without expanding ordinary agent context.
|
|
167
|
+
|
|
168
|
+
## Declared visibility and completed destinations
|
|
169
|
+
|
|
170
|
+
Tracked by [pm-34xisz](../.agents/pm/issues/pm-34xisz.toon),
|
|
171
|
+
[pm-3ig9b6](../.agents/pm/issues/pm-3ig9b6.toon), and
|
|
172
|
+
[pm-djyvbk](../.agents/pm/tasks/pm-djyvbk.toon).
|
|
173
|
+
|
|
174
|
+
A package command's declared `tier` controls SDK runtime discovery. Internal
|
|
175
|
+
commands and actions are omitted from default contracts, summaries, and schemas;
|
|
176
|
+
full or explicitly scoped inspection retains them and their declared tier.
|
|
177
|
+
Packages need no entry in a core-only visibility list.
|
|
178
|
+
|
|
179
|
+
`history acknowledge` is the canonical author-disposition command. The permanent
|
|
180
|
+
`history-author-acknowledge` alias keeps the same preview/apply handshake, flags,
|
|
181
|
+
results, and SDK/MCP action identity. `assurance` retains its established surface
|
|
182
|
+
as a reasoned exception: measurements, assertions, gates, and verdicts are
|
|
183
|
+
persistent project data, with operations beyond diagnostics.
|
|
184
|
+
|
|
185
|
+
Consolidation rows are checked against a complete all-status tracker snapshot.
|
|
186
|
+
A missing, closed, or canceled owner fails the grammar gate unless a declared
|
|
187
|
+
alias already resolves the spelling to a live canonical command. Delivered
|
|
188
|
+
leaves are classified as target paths; deliberate concise item operations carry
|
|
189
|
+
explicit keep-as-is reasons. Closing an owner cannot silently certify unfinished
|
|
190
|
+
command migration.
|
|
191
|
+
|
|
192
|
+
Full discovery uses a uniform destination table. Its `reason` column contains
|
|
193
|
+
the rationale for exceptions and an empty string where no exception applies,
|
|
194
|
+
so adding a rationale does not expand every row into repeated field names.
|
|
@@ -40,10 +40,13 @@ pm contracts --command "ops stats" --flags-only
|
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Help, contracts, and shell completion expose native paths. Compatibility aliases
|
|
43
|
-
remain declared in the SDK alias table
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
remain declared in the SDK alias table with a `lifecycle` field. The root
|
|
44
|
+
spellings in the table above are `permanent` aliases: they are ergonomic API and
|
|
45
|
+
emit nothing on stderr. Only `deprecated` spellings (for example `list-open` or
|
|
46
|
+
`start-task`) emit the one-line migration hint, which the existing
|
|
47
|
+
`ux.deprecation_hints` setting suppresses and which JSON and quiet calls never
|
|
48
|
+
print. Compact contract summaries omit duplicate root alias rows; full and
|
|
49
|
+
explicitly selected contracts retain them. Optional commands appear only when their providing package is active;
|
|
47
50
|
namespace placement does not promote package behavior into core.
|
|
48
51
|
|
|
49
52
|
## SDK and package integration
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -150,29 +150,74 @@ Tests should set both `PM_PATH` and `PM_GLOBAL_PATH` to temporary directories. T
|
|
|
150
150
|
|
|
151
151
|
### Telemetry environment variables
|
|
152
152
|
|
|
153
|
-
Telemetry is
|
|
153
|
+
Telemetry is enabled by default. Use `pm config set telemetry-tracking off` to opt out persistently (see [Common Settings](#common-settings)). When enabled, these environment variables tune runtime behaviour. All boolean knobs accept `1`, `true`, `yes`, or `on` (case-insensitive); any other value leaves the knob off.
|
|
154
154
|
|
|
155
155
|
| Variable | Values | Use |
|
|
156
156
|
| ------------------------------------ | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
157
|
| `PM_TELEMETRY_DISABLED` | boolean | Hard-disable all telemetry for this process, ignoring settings. |
|
|
158
|
+
| `DO_NOT_TRACK` | boolean | Hard-disable event capture, identity creation, flush workers, endpoint probes, and Sentry, regardless of persisted preferences. |
|
|
159
|
+
| `PM_TELEMETRY_SEND_TEST_EVENTS` | boolean | Explicitly allow inferred test events. Hard opt-outs still take precedence. |
|
|
160
|
+
| `PM_TELEMETRY_READ_SAMPLE_RATE` | number greater than `0` and at most `1` | Opt-in inclusion probability for successful core-only reads; omitted or invalid values retain every invocation. |
|
|
158
161
|
| `PM_NO_TELEMETRY` | boolean | Alias for `PM_TELEMETRY_DISABLED` (honoured by the same checks). |
|
|
159
162
|
| `PM_TELEMETRY_OTEL_DISABLED` | boolean | Disable only OTLP trace-span export; the event queue still flushes. |
|
|
160
163
|
| `PM_TELEMETRY_INLINE_FLUSH` | boolean | Flush the queue and OTLP spans inline instead of dispatching the detached worker. Mainly for tests; normal use relies on the background worker. |
|
|
161
|
-
| `PM_TELEMETRY_SOURCE_CONTEXT` | `user` \| `automation` \| `test` \| `dogfood` | Override the inferred source context recorded on each event.
|
|
164
|
+
| `PM_TELEMETRY_SOURCE_CONTEXT` | `user` \| `automation` \| `test` \| `dogfood` | Override the inferred source context recorded on each event. Unrecognized nonempty values use inferred context with `source_context_source=env_override_rejected`; the raw value is never emitted. |
|
|
162
165
|
| `PM_TELEMETRY_HTTP_TIMEOUT_MS` | integer milliseconds | Bound each background event or OTLP request (default `20000`, clamped to `1000`–`25000` to stay below the worker lock TTL). |
|
|
163
166
|
| `PM_TELEMETRY_INGEST_KEY` | string | Sent as the `x-pm-telemetry-key` header on queue flushes; never logged. |
|
|
164
167
|
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | URL | OTLP/HTTP traces endpoint for command spans. Takes precedence over the base endpoint. |
|
|
165
168
|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | URL | Base OTLP endpoint; the traces endpoint is derived by appending `/v1/traces`. |
|
|
166
169
|
| `OTEL_SERVICE_NAME` | string | `service.name` attribute on exported spans (defaults to `pm-cli`). |
|
|
167
170
|
|
|
171
|
+
Unrecognized source-context overrides produce one bounded warning per process on
|
|
172
|
+
stderr, naming the accepted values without echoing the rejected input. JSON
|
|
173
|
+
invocations retain parseable stdout and receive the warning on stderr. Explicit
|
|
174
|
+
quiet mode suppresses the warning; events and spans still carry the fixed
|
|
175
|
+
rejection marker. Empty overrides mean no override. Existing consent
|
|
176
|
+
rules continue to suppress all capture when telemetry is disabled.
|
|
177
|
+
|
|
178
|
+
Event and OTLP queue appends and reconciliation share an installation-level
|
|
179
|
+
mutex. Successful delivery, retry updates, and retention pruning reread the
|
|
180
|
+
current queue while holding that mutex, so another updated CLI process cannot
|
|
181
|
+
append into a file about to be replaced. Network delivery runs outside the
|
|
182
|
+
mutex. Each foreground start, finish, or error capture uses a 250 ms
|
|
183
|
+
lock-acquisition budget by default; queue maintenance, clear, and flush initialization
|
|
184
|
+
retain a five-second default. `PM_LOCK_WAIT_MS` explicitly overrides these
|
|
185
|
+
budgets. These are per-acquisition contention budgets, not total operation
|
|
186
|
+
deadlines: queued in-process transactions and filesystem work can add latency.
|
|
187
|
+
The existing 60-second stale-lock recovery policy applies. Telemetry remains best
|
|
188
|
+
effort when storage is unavailable or contention exhausts the wait budget;
|
|
189
|
+
collector success and an empty queue are separate observations, not a guarantee
|
|
190
|
+
of exactly-once delivery. Upgrade concurrent CLI processes together: older
|
|
191
|
+
versions do not participate in the installation mutex.
|
|
192
|
+
|
|
193
|
+
Start and error capture recheck process opt-outs and saved consent under the
|
|
194
|
+
installation mutex before initializing identity, and retain that mutex through
|
|
195
|
+
immediate event writes. Completion rechecks both consent sources and its saved
|
|
196
|
+
installation identity under the same mutex through deferred start, finish,
|
|
197
|
+
and span writes. `telemetry clear` uses this transaction boundary, so it either
|
|
198
|
+
removes a completed capture or prevents that capture from appending afterward.
|
|
199
|
+
Flush identity initialization also revalidates consent under the mutex; network
|
|
200
|
+
delivery and flush scheduling remain outside it. Preference changes are
|
|
201
|
+
evaluated at the capture transaction's consent check. Re-enabling after a clear
|
|
202
|
+
does not revive an invocation's old identity. These checks cannot recall
|
|
203
|
+
requests already dispatched to a collector.
|
|
204
|
+
|
|
168
205
|
Interaction rules:
|
|
169
206
|
|
|
170
|
-
- `PM_TELEMETRY_DISABLED` / `PM_NO_TELEMETRY` short-circuit everything, including OTLP export, regardless of the other knobs.
|
|
207
|
+
- `DO_NOT_TRACK` / `PM_TELEMETRY_DISABLED` / `PM_NO_TELEMETRY` short-circuit everything, including OTLP export, regardless of the other knobs.
|
|
171
208
|
- `PM_TELEMETRY_HTTP_TIMEOUT_MS` applies only to the detached flush worker in normal use. It covers DNS, each IPv4/IPv6 connection attempt, TLS, and response completion. The upper bound preserves the 60-second worker-lock safety margin across the sequential event and OTLP phases; it does not make foreground commands wait for the network.
|
|
172
209
|
- OTLP span export only happens when telemetry is enabled, `PM_TELEMETRY_OTEL_DISABLED` is off, and a traces endpoint is configured. By default spans are persisted to a bounded queue and exported by the detached, unref'd flush worker so commands exit promptly even when the traces endpoint is unreachable. `PM_TELEMETRY_INLINE_FLUSH=1` is the explicit test-oriented exception that performs the flush inline.
|
|
173
210
|
- `pm health --check-telemetry --json` surfaces flush and OTLP export diagnostics (`pending_otel_spans`, `last_otel_attempt_at`, `last_otel_success_at`, `last_otel_failure_at`, `last_otel_failure_error`) and the active `env_overrides` (including `telemetry_inline_flush` and `telemetry_source_context`) so agents can self-diagnose a stalled endpoint.
|
|
174
211
|
- An explicit `PM_AUTHOR` override adds a privacy-preserving agent-identity dimension to `command_start`/`command_finish` events so overridden invocations can be segmented in dashboards without leaking the raw author string. At `redacted`/`max` capture the events carry `author_context_hash` — the same installation-id-keyed one-way SHA-256 used for `pm_root_hash`/`cwd_hash`, so the same author hashes consistently within an installation but differently across installations. At `minimal` capture only a boolean `has_author_context` is emitted. The raw `PM_AUTHOR` value is never exported.
|
|
175
212
|
|
|
213
|
+
Test environments inferred from `VITEST`, `VITEST_WORKER_ID`, or `NODE_ENV=test` suppress telemetry before identity creation. A valid explicit `PM_TELEMETRY_SOURCE_CONTEXT` or `PM_TELEMETRY_SEND_TEST_EVENTS=1` permits intentional test delivery. Point such tests at a local collector. Neither setting overrides a hard opt-out. `pm telemetry status` exposes effective `enabled`, persisted `configured_enabled`, and `env_overrides` so an empty queue is distinguishable from disabled delivery.
|
|
214
|
+
|
|
215
|
+
SDK consumers that previously used `TelemetryStatusSummary.enabled` to display the saved preference should read `configured_enabled` instead. Use `enabled` to determine whether this process permits delivery.
|
|
216
|
+
|
|
217
|
+
At `redacted` and `max` capture, events include a bounded `agent_harness` dimension (`claude-code`, `codex`, `pi`, `opencode`, `cursor`, `aider`, `gemini-cli`, `ci`, `other`, or `none`) and a boolean `ci`. Installation-keyed hashes remain available; raw author and model names remain private. Minimal capture omits both new dimensions.
|
|
218
|
+
|
|
219
|
+
For repeated read loops, set `PM_TELEMETRY_READ_SAMPLE_RATE=0.1` and use `--no-extensions`. Sampling applies only to `stats`, `ops stats`, `list`, `get`, `next`, `context`, `activity`, and `aggregate`. Mutations, unknown commands, extension-enabled invocations, and failures are retained. Successful sampled reads defer their start until completion so both lifecycle events are retained or dropped together; abruptly terminated sampled reads have no event pair. Retained pairs carry numeric `sample_rate`, and OTLP spans carry `pm.sample_rate`; estimate population totals with weight `1 / sample_rate`. Failed sampled reads carry probability `1`. Local queue statistics describe retained observations, not weighted population estimates.
|
|
220
|
+
|
|
176
221
|
`pm telemetry stats` reads the local queue and reports, per command bucket, latency percentiles (`duration_p50_ms`/`duration_p95_ms`/`duration_max_ms`, nearest-rank over `command_finish` `duration_ms`), outcome rates (`ok_count`/`error_count`/`error_rate`; a finish event whose `ok` is missing or not strictly `true` is counted conservatively as an error), and `command_resolution_counts`. These are an always-available, zero-network performance and reliability signal for the most recent queued window.
|
|
177
222
|
|
|
178
223
|
### Sentry environment variables
|
|
@@ -7,11 +7,11 @@ This file is generated from `PM_COMMAND_CAPABILITY_CONTRACTS`. Do not edit it ma
|
|
|
7
7
|
| Capability family | Commands |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| workspace | `init`, `config`, `workspace merge`, `schema`, `profile`, `ops telemetry`, `ops health`, `ops gc`, `workspace` |
|
|
10
|
-
| intake | `create`, `item copy`, `context focus`, `history restore` |
|
|
10
|
+
| intake | `create`, `item`, `item copy`, `context focus`, `history restore` |
|
|
11
11
|
| context | `list`, `aggregate`, `context`, `get`, `search`, `item duplicates`, `ops eval`, `context next`, `history activity`, `ops stats`, `help` |
|
|
12
12
|
| lifecycle | `update`, `update many`, `close`, `close many`, `close delete`, `claim`, `release` |
|
|
13
|
-
| evidence | `history`, `history events`, `history redact`, `history repair`, `history compact`, `history attest`, `history
|
|
14
|
-
| graph | `graph`, `deps`, `plan` |
|
|
15
|
-
| quality | `test`, `ops test-all`, `ops validate`, `assurance`, `contracts`, `ops` |
|
|
13
|
+
| evidence | `history`, `history events`, `history redact`, `history repair`, `history compact`, `history attest`, `history acknowledge`, `item append`, `item comments`, `item notes`, `item learnings`, `item files`, `item docs` |
|
|
14
|
+
| graph | `graph`, `item deps`, `plan` |
|
|
15
|
+
| quality | `item test`, `ops test-all`, `ops validate`, `assurance`, `contracts`, `ops` |
|
|
16
16
|
| automation | `meet`, `event`, `remind` |
|
|
17
17
|
| extensions | `package` |
|
|
@@ -12,6 +12,7 @@ This file is generated from `PM_COMMAND_CAPABILITY_CONTRACTS`. Do not edit it ma
|
|
|
12
12
|
| `install` | full | extensions |
|
|
13
13
|
| `upgrade` | full | extensions |
|
|
14
14
|
| `create` | core | intake |
|
|
15
|
+
| `item` | full | intake |
|
|
15
16
|
| `item-reopen` | full | lifecycle |
|
|
16
17
|
| `copy` | full | intake |
|
|
17
18
|
| `item copy` | full | intake |
|
|
@@ -49,6 +50,7 @@ This file is generated from `PM_COMMAND_CAPABILITY_CONTRACTS`. Do not edit it ma
|
|
|
49
50
|
| `history-attest` | full | evidence |
|
|
50
51
|
| `history attest` | full | evidence |
|
|
51
52
|
| `history-author-acknowledge` | full | evidence |
|
|
53
|
+
| `history acknowledge` | full | evidence |
|
|
52
54
|
| `merge` | full | workspace |
|
|
53
55
|
| `workspace merge` | full | workspace |
|
|
54
56
|
| `schema` | standard | workspace |
|
|
@@ -66,14 +68,22 @@ This file is generated from `PM_COMMAND_CAPABILITY_CONTRACTS`. Do not edit it ma
|
|
|
66
68
|
| `delete` | full | lifecycle |
|
|
67
69
|
| `close delete` | full | lifecycle |
|
|
68
70
|
| `append` | standard | evidence |
|
|
71
|
+
| `item append` | standard | evidence |
|
|
69
72
|
| `comments` | standard | evidence |
|
|
73
|
+
| `item comments` | standard | evidence |
|
|
70
74
|
| `notes` | standard | evidence |
|
|
75
|
+
| `item notes` | standard | evidence |
|
|
71
76
|
| `learnings` | standard | evidence |
|
|
77
|
+
| `item learnings` | standard | evidence |
|
|
72
78
|
| `files` | standard | evidence |
|
|
79
|
+
| `item files` | standard | evidence |
|
|
73
80
|
| `docs` | standard | evidence |
|
|
81
|
+
| `item docs` | standard | evidence |
|
|
74
82
|
| `deps` | standard | graph |
|
|
83
|
+
| `item deps` | standard | graph |
|
|
75
84
|
| `plan` | core | graph |
|
|
76
85
|
| `test` | standard | quality |
|
|
86
|
+
| `item test` | standard | quality |
|
|
77
87
|
| `test-all` | full | quality |
|
|
78
88
|
| `ops test-all` | full | quality |
|
|
79
89
|
| `telemetry` | full | workspace |
|
|
@@ -103,3 +113,4 @@ This file is generated from `PM_COMMAND_CAPABILITY_CONTRACTS`. Do not edit it ma
|
|
|
103
113
|
| `completion-tags` | internal | internal |
|
|
104
114
|
| `completion-types` | internal | internal |
|
|
105
115
|
| `test-runs-worker` | internal | internal |
|
|
116
|
+
| `item test worker` | internal | internal |
|
|
@@ -5,6 +5,7 @@ This file is generated by `listPmFlagLexicon()`. Compatibility aliases do not co
|
|
|
5
5
|
| Command | Capability family | Canonical flags | Maximum |
|
|
6
6
|
| --- | --- | ---: | ---: |
|
|
7
7
|
| `init` | workspace | 30 | 30 |
|
|
8
|
+
| `item` | intake | 20 | 20 |
|
|
8
9
|
| `config` | workspace | 36 | 36 |
|
|
9
10
|
| `extension` | extensions | 54 | 54 |
|
|
10
11
|
| `package` | extensions | 55 | 55 |
|
|
@@ -74,7 +75,7 @@ This file is generated by `listPmFlagLexicon()`. Compatibility aliases do not co
|
|
|
74
75
|
| `meet` | automation | 33 | 33 |
|
|
75
76
|
| `event` | automation | 33 | 33 |
|
|
76
77
|
| `remind` | automation | 29 | 29 |
|
|
77
|
-
| `test-runs-worker` | internal |
|
|
78
|
+
| `test-runs-worker` | internal | 20 | 20 |
|
|
78
79
|
| `history attest` | evidence | 23 | 23 |
|
|
79
80
|
| `history redact` | evidence | 27 | 27 |
|
|
80
81
|
| `history repair` | evidence | 27 | 27 |
|
|
@@ -94,6 +95,16 @@ This file is generated by `listPmFlagLexicon()`. Compatibility aliases do not co
|
|
|
94
95
|
| `update many` | lifecycle | 146 | 146 |
|
|
95
96
|
| `close many` | lifecycle | 72 | 72 |
|
|
96
97
|
| `close delete` | lifecycle | 24 | 24 |
|
|
98
|
+
| `item comments` | evidence | 31 | 31 |
|
|
99
|
+
| `item notes` | evidence | 35 | 35 |
|
|
100
|
+
| `item learnings` | evidence | 31 | 31 |
|
|
101
|
+
| `item files` | evidence | 39 | 39 |
|
|
102
|
+
| `item docs` | evidence | 30 | 30 |
|
|
103
|
+
| `item deps` | graph | 32 | 32 |
|
|
104
|
+
| `item append` | evidence | 25 | 25 |
|
|
105
|
+
| `item test` | quality | 53 | 53 |
|
|
106
|
+
| `item test worker` | internal | 20 | 20 |
|
|
107
|
+
| `history acknowledge` | evidence | 28 | 28 |
|
|
97
108
|
| `item copy` | intake | 24 | 24 |
|
|
98
109
|
| `workspace merge` | workspace | 27 | 27 |
|
|
99
110
|
| `item duplicates` | context | 25 | 25 |
|
|
@@ -11,7 +11,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
|
|
|
11
11
|
- Uncovered error codes: 355
|
|
12
12
|
- Coverage fraction: 0.050802
|
|
13
13
|
- Closed-domain probes: 19
|
|
14
|
-
- Grammar probes:
|
|
14
|
+
- Grammar probes: 113
|
|
15
15
|
|
|
16
16
|
| Error code | Canonical code | Disposition | Evidence kinds | Probe count |
|
|
17
17
|
| --- | --- | --- | --- | --- |
|
|
@@ -214,7 +214,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
|
|
|
214
214
|
| `missing_parameter_alias` | `missing_parameter_alias` | uncovered | none | 0 |
|
|
215
215
|
| `missing_positive_execution_receipt` | `missing_positive_execution_receipt` | uncovered | none | 0 |
|
|
216
216
|
| `missing_probe` | `missing_probe` | uncovered | none | 0 |
|
|
217
|
-
| `missing_required_argument` | `missing_required_argument` | executable | grammar |
|
|
217
|
+
| `missing_required_argument` | `missing_required_argument` | executable | grammar | 75 |
|
|
218
218
|
| `missing_required_option` | `missing_required_option` | uncovered | none | 0 |
|
|
219
219
|
| `missing_suggested_retry` | `missing_suggested_retry` | uncovered | none | 0 |
|
|
220
220
|
| `missing_suggested_retry_args` | `missing_suggested_retry_args` | uncovered | none | 0 |
|
package/marketplace.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
|
|
9
|
-
"version": "2026.9.
|
|
9
|
+
"version": "2026.9.14"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "pm-claude",
|
|
14
14
|
"source": "./plugins/pm-claude",
|
|
15
15
|
"description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
|
|
16
|
-
"version": "2026.9.
|
|
16
|
+
"version": "2026.9.14",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "unbrained",
|
|
19
19
|
"url": "https://github.com/unbraind/pm-cli"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-cli",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.14",
|
|
4
4
|
"description": "Git-native project management CLI for humans and agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.10.0",
|
|
@@ -213,25 +213,25 @@
|
|
|
213
213
|
"fast-json-patch": "^3.1.1",
|
|
214
214
|
"npm-package-arg": "^13.0.2",
|
|
215
215
|
"tar": "7.5.22",
|
|
216
|
-
"yaml": "^2.9.
|
|
216
|
+
"yaml": "^2.9.1"
|
|
217
217
|
},
|
|
218
218
|
"devDependencies": {
|
|
219
219
|
"@codspeed/vitest-plugin": "^5.7.1",
|
|
220
220
|
"@eslint/js": "^10.0.1",
|
|
221
221
|
"@modelcontextprotocol/ext-apps": "2.0.0",
|
|
222
222
|
"@sentry/cli": "^3.7.0",
|
|
223
|
-
"@types/node": "^26.5.
|
|
223
|
+
"@types/node": "^26.5.1",
|
|
224
224
|
"@types/npm-package-arg": "^6.1.4",
|
|
225
225
|
"@vitest/coverage-v8": "^4.1.11",
|
|
226
226
|
"esbuild": "0.28.2",
|
|
227
227
|
"eslint": "^10.10.0",
|
|
228
228
|
"eslint-plugin-sonarjs": "^4.2.0",
|
|
229
229
|
"eslint-plugin-unicorn": "74.0.0",
|
|
230
|
-
"fast-check": "^4.
|
|
230
|
+
"fast-check": "^4.10.0",
|
|
231
231
|
"github-slugger": "2.0.0",
|
|
232
232
|
"greptile": "^3.5.2",
|
|
233
233
|
"jscpd": "^5.2.0",
|
|
234
|
-
"marked": "18.0.
|
|
234
|
+
"marked": "18.0.13",
|
|
235
235
|
"parse5": "8.0.1",
|
|
236
236
|
"tsx": "^4.23.13",
|
|
237
237
|
"typescript": "^6.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-digital-twin",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.14",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
|
|
@@ -126,50 +126,37 @@ function completionCommand(): CommandDefinition {
|
|
|
126
126
|
runCompletionPackage(context.args, context.options, context.global),
|
|
127
127
|
};
|
|
128
128
|
}
|
|
129
|
-
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
/** Registers this package's commands, actions, and runtime hooks with the host. */
|
|
130
|
+
export function activate(api: ExtensionApi): void {
|
|
131
|
+
api.registerCommand(guideCommand());
|
|
132
|
+
api.registerCommand(completionCommand());
|
|
133
|
+
api.registerCommand({
|
|
134
|
+
name: "completion tags",
|
|
133
135
|
action: "completion-tags",
|
|
134
136
|
tier: "internal",
|
|
135
137
|
family: "internal",
|
|
136
138
|
description: "Print known tags for completion filters.",
|
|
137
139
|
intent: "List tag values for lazy shell completion.",
|
|
138
140
|
run: async (context) => runCompletionTagsPackage(context.global),
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
function completionStatusesCommand(): CommandDefinition {
|
|
143
|
-
return {
|
|
144
|
-
name: "completion-statuses",
|
|
141
|
+
});
|
|
142
|
+
api.registerCommand({
|
|
143
|
+
name: "completion statuses",
|
|
145
144
|
action: "completion-statuses",
|
|
146
145
|
tier: "internal",
|
|
147
146
|
family: "internal",
|
|
148
147
|
description: "Print runtime status IDs for completion filters.",
|
|
149
148
|
intent: "List runtime status values for shell completion.",
|
|
150
149
|
run: async (context) => runCompletionStatusesPackage(context.global),
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
function completionTypesCommand(): CommandDefinition {
|
|
155
|
-
return {
|
|
156
|
-
name: "completion-types",
|
|
150
|
+
});
|
|
151
|
+
api.registerCommand({
|
|
152
|
+
name: "completion types",
|
|
157
153
|
action: "completion-types",
|
|
158
154
|
tier: "internal",
|
|
159
155
|
family: "internal",
|
|
160
156
|
description: "Print runtime item type IDs for completion filters.",
|
|
161
157
|
intent: "List runtime item types for shell completion.",
|
|
162
158
|
run: async (context) => runCompletionTypesPackage(context.global),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Registers this package's commands, actions, and runtime hooks with the host. */
|
|
167
|
-
export function activate(api: ExtensionApi): void {
|
|
168
|
-
api.registerCommand(guideCommand());
|
|
169
|
-
api.registerCommand(completionCommand());
|
|
170
|
-
api.registerCommand(completionTagsCommand());
|
|
171
|
-
api.registerCommand(completionStatusesCommand());
|
|
172
|
-
api.registerCommand(completionTypesCommand());
|
|
159
|
+
});
|
|
173
160
|
api.registerService("output_format", (context) => {
|
|
174
161
|
const rendered = renderGuideShellPackageOutput(
|
|
175
162
|
context as ServiceOverrideContext,
|