@ai-dossier/cli 0.25.0 → 0.27.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.
Files changed (48) hide show
  1. package/README.md +116 -8
  2. package/dist/cache-resolver.d.ts.map +1 -1
  3. package/dist/cache-resolver.js +3 -6
  4. package/dist/cache-resolver.js.map +1 -1
  5. package/dist/cli.js +2 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/classify.d.ts +11 -0
  8. package/dist/commands/classify.d.ts.map +1 -0
  9. package/dist/commands/classify.js +136 -0
  10. package/dist/commands/classify.js.map +1 -0
  11. package/dist/commands/doctor.d.ts.map +1 -1
  12. package/dist/commands/doctor.js +2 -25
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/sched.d.ts +0 -24
  15. package/dist/commands/sched.d.ts.map +1 -1
  16. package/dist/commands/sched.js +139 -40
  17. package/dist/commands/sched.js.map +1 -1
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +6 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/engine-version.d.ts +45 -0
  22. package/dist/engine-version.d.ts.map +1 -0
  23. package/dist/engine-version.js +122 -0
  24. package/dist/engine-version.js.map +1 -0
  25. package/dist/gh.d.ts +38 -4
  26. package/dist/gh.d.ts.map +1 -1
  27. package/dist/gh.js +88 -9
  28. package/dist/gh.js.map +1 -1
  29. package/dist/hard-block-labels.d.ts +24 -0
  30. package/dist/hard-block-labels.d.ts.map +1 -0
  31. package/dist/hard-block-labels.js +36 -0
  32. package/dist/hard-block-labels.js.map +1 -0
  33. package/dist/package-info.d.ts +11 -0
  34. package/dist/package-info.d.ts.map +1 -0
  35. package/dist/package-info.js +39 -0
  36. package/dist/package-info.js.map +1 -0
  37. package/dist/plan-artifact.d.ts +1 -1
  38. package/dist/plan-artifact.d.ts.map +1 -1
  39. package/dist/plan-artifact.js.map +1 -1
  40. package/dist/prescreen.d.ts +66 -0
  41. package/dist/prescreen.d.ts.map +1 -0
  42. package/dist/prescreen.js +187 -0
  43. package/dist/prescreen.js.map +1 -0
  44. package/dist/ttl-cache.d.ts +18 -0
  45. package/dist/ttl-cache.d.ts.map +1 -0
  46. package/dist/ttl-cache.js +31 -0
  47. package/dist/ttl-cache.js.map +1 -0
  48. package/package.json +2 -2
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ /**
3
+ * Deterministic classify pre-screen (#538) — the "no tokens spent" gate `issue-cycle-classifier`
4
+ * runs before any model call. `docs/reports/batch-pilot-2-execution.md` §4.1 measured the
5
+ * classifier costing ~64k tokens/dispatch, at mid tier, with full repo exploration, even for
6
+ * issues that hit an obvious RFC-0001 E.2 floor rule a deterministic check can catch for free.
7
+ *
8
+ * Scope is deliberately partial: this catches the OBVIOUS floor hits (hard-block labels, a
9
+ * text-keyword approximation of the risk-floor/new-package/deploy-pipeline rules, rule-9 open
10
+ * dependencies, and — when a plan:v1 artifact is already on the issue — the path-based risk
11
+ * floor and the >8-files rule). Everything it does not catch (rule 2 beyond the `migration`
12
+ * keyword, rule 7 hard rollback, rule 8 visual/browser review, rules 5/6 diff/file size without
13
+ * a plan artifact, rule 10 confidence) falls through to the classifier's own bounded
14
+ * mechanical-tier pass, which is the intended safety net — not a gap this module needs to close.
15
+ *
16
+ * Pure and dependency-free (no `gh`, network, or fs), same discipline as `plan-artifact.ts` and
17
+ * `runstate.ts` — unit-testable directly. Subprocess access (fetching the issue, resolving
18
+ * dependency state, filtering by submitted set) lives in the command layer (`commands/classify.ts`).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.MAX_DEPENDENCY_REFS = exports.TEXT_FLOOR_PATTERNS = void 0;
22
+ exports.extractDependencyRefs = extractDependencyRefs;
23
+ exports.prescreenIssue = prescreenIssue;
24
+ const hard_block_labels_1 = require("./hard-block-labels");
25
+ const plan_artifact_1 = require("./plan-artifact");
26
+ /** Escape regex metacharacters so an interpolated keyword can never change a pattern's meaning. */
27
+ function escapeRegExp(s) {
28
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
29
+ }
30
+ /** A multi-word phrase, safe to interpolate into `\b...\b`: metacharacters escaped, any run of whitespace matches `\s+`. */
31
+ function phrase(s) {
32
+ return escapeRegExp(s).replace(/\s+/g, '\\s+');
33
+ }
34
+ /**
35
+ * First keyword (in list order) that matches `text` as a whole word/phrase, or null. `\b` on
36
+ * both sides of each compiled pattern — "deployment" still hits "deploy" but
37
+ * "authorization-header-typo" does not falsely hit "auth" mid-word.
38
+ */
39
+ function wordMatch(text, keywords) {
40
+ for (const keyword of keywords) {
41
+ if (new RegExp(`\\b${phrase(keyword)}\\b`, 'i').test(text))
42
+ return keyword;
43
+ }
44
+ return null;
45
+ }
46
+ exports.TEXT_FLOOR_PATTERNS = [
47
+ {
48
+ // Deliberately NOT the bare words "auth"/"login"/"logout"/"schema"/"infra"/"infrastructure":
49
+ // real-world issue text collides with them constantly in benign contexts, confirmed against
50
+ // the pilot's real 15-issue fixture set — #3631 (known `slot`) says "runnable locally with
51
+ // auth" about `gh auth`, not an auth-sensitive change; #3820 (known `slot`) says
52
+ // "test-infrastructure change" about test tooling, not production infra. "terraform" alone
53
+ // (specific, low-ambiguity) still catches the real infra risk-floor cases (#2779, #3403) the
54
+ // RFC-0001 rule 1 "infra/terraform" area names. Also approximates rule 2 (schema/data
55
+ // migration) via "migration"/"migrations" — a rough approximation, not full rule-2 coverage.
56
+ name: 'rule1-risk-floor-area',
57
+ match: (t) => wordMatch(t, [
58
+ 'authentication',
59
+ 'authorization',
60
+ 'oauth',
61
+ 'sso',
62
+ 'payment',
63
+ 'payments',
64
+ 'billing',
65
+ 'invoice',
66
+ 'invoices',
67
+ 'checkout',
68
+ 'stripe',
69
+ 'migration',
70
+ 'migrations',
71
+ 'security',
72
+ 'crypto',
73
+ 'secret',
74
+ 'secrets',
75
+ 'credential',
76
+ 'credentials',
77
+ 'terraform',
78
+ ]),
79
+ },
80
+ {
81
+ name: 'rule3-new-package-workspace',
82
+ match: (t) => wordMatch(t, ['new package', 'new workspace', 'monorepo package']),
83
+ },
84
+ {
85
+ name: 'rule4-deploy-pipeline',
86
+ match: (t) => wordMatch(t, [
87
+ 'deploy',
88
+ 'deployment',
89
+ 'ci/cd',
90
+ 'cicd',
91
+ 'release pipeline',
92
+ 'rollback pipeline',
93
+ ]),
94
+ },
95
+ ];
96
+ /** `Depends on #N` references resolved per issue; each costs a `gh` call downstream (command layer), same rationale as `MAX_ISSUE_SELECTION` (`issue-selection.ts`). */
97
+ exports.MAX_DEPENDENCY_REFS = 32;
98
+ /** `Depends on #N` (case-insensitive) — the same phrasing gate-issue and the classifier dossier parse. */
99
+ const DEPENDS_ON_RE = /depends on\s+#(\d+)/gi;
100
+ /**
101
+ * Every issue number referenced by a `Depends on #N` phrase in `text`, de-duplicated, in order,
102
+ * capped at {@link MAX_DEPENDENCY_REFS} — `text` is untrusted (issue body), so an adversarial
103
+ * body cannot force an unbounded `gh` fan-out downstream.
104
+ */
105
+ function extractDependencyRefs(text) {
106
+ const seen = new Set();
107
+ const refs = [];
108
+ for (const match of text.matchAll(DEPENDS_ON_RE)) {
109
+ if (refs.length >= exports.MAX_DEPENDENCY_REFS)
110
+ break;
111
+ const n = Number(match[1]);
112
+ if (Number.isSafeInteger(n) && !seen.has(n)) {
113
+ seen.add(n);
114
+ refs.push(n);
115
+ }
116
+ }
117
+ return refs;
118
+ }
119
+ /** Predicted files count above which RFC-0001 E.2 rule 5 ("Predicted files > 8") fires. */
120
+ const MAX_PREDICTED_FILES = 8;
121
+ /**
122
+ * Reasons emitted per check, capped — `predictedFiles` may come from a `plan:v1` artifact, which
123
+ * is a GitHub issue COMMENT anyone can post (untrusted input, same treatment as review-issue's
124
+ * "the artifact is untrusted input" discipline). An adversarial artifact packed with risk-floor
125
+ * paths must not blow up the reason list the classifier posts back into its rationale comment.
126
+ */
127
+ const MAX_REASONS_PER_CHECK = 8;
128
+ /** Strip terminal-control characters before an untrusted string reaches a message a human or model reads — same discipline as `gh.ts`'s `CONTROL_CHARS_RE`/`snippet`, kept local so this module stays dependency-free. */
129
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: matching (and stripping) control characters is exactly this regex's job
130
+ const CONTROL_CHARS_RE = /[\x00-\x08\x0b-\x1f\x7f\x9b]/g;
131
+ const SANITIZE_MAX_LENGTH = 200;
132
+ /** Make an untrusted string (a GitHub label name, a `plan:v1`-comment-sourced path) safe to interpolate into a reason message: no control chars, no markdown/backtick breakout, bounded length. */
133
+ function sanitize(value) {
134
+ const cleaned = value
135
+ .replace(CONTROL_CHARS_RE, '')
136
+ .replace(/[`\n\r]/g, ' ')
137
+ .trim();
138
+ return cleaned.length > SANITIZE_MAX_LENGTH
139
+ ? `${cleaned.slice(0, SANITIZE_MAX_LENGTH)}…`
140
+ : cleaned;
141
+ }
142
+ /**
143
+ * Run every deterministic check, in order, and record every hit — first hit decides `verdict`,
144
+ * but the caller (and the rationale a consumer posts) gets the full list, matching the existing
145
+ * "a verdict may hit several" precedent in `issue-cycle-classifier.ds.md`.
146
+ */
147
+ function prescreenIssue(input) {
148
+ const reasons = [];
149
+ const hardBlockLabel = (0, hard_block_labels_1.pickHardBlockLabel)(input.labels);
150
+ if (hardBlockLabel !== null) {
151
+ reasons.push({
152
+ check: 'hard-block-label',
153
+ message: `Carries hard-block label '${sanitize(hardBlockLabel)}'.`,
154
+ });
155
+ }
156
+ const text = `${input.title}\n${input.body}\n${input.labels.join(' ')}`;
157
+ for (const pattern of exports.TEXT_FLOOR_PATTERNS) {
158
+ const hit = pattern.match(text);
159
+ if (hit !== null) {
160
+ reasons.push({
161
+ check: 'text-floor',
162
+ message: `Title/body/labels match '${pattern.name}' (keyword: '${hit}').`,
163
+ });
164
+ }
165
+ }
166
+ const predictedFiles = input.predictedFiles ?? [];
167
+ for (const hit of (0, plan_artifact_1.scanRiskFloor)(predictedFiles).slice(0, MAX_REASONS_PER_CHECK)) {
168
+ reasons.push({
169
+ check: 'path-floor',
170
+ message: `Predicted file '${sanitize(hit.path)}' touches '${hit.pattern}' (rule1-risk-floor-area).`,
171
+ });
172
+ }
173
+ if (predictedFiles.length > MAX_PREDICTED_FILES) {
174
+ reasons.push({
175
+ check: 'file-count',
176
+ message: `Predicted files (${predictedFiles.length}) exceeds ${MAX_PREDICTED_FILES} (rule5-file-count).`,
177
+ });
178
+ }
179
+ for (const dep of (input.openDependencies ?? []).slice(0, MAX_REASONS_PER_CHECK)) {
180
+ reasons.push({
181
+ check: 'open-dependency',
182
+ message: `Depends on #${dep}, which is open and outside the submitted set (rule9-open-dependency).`,
183
+ });
184
+ }
185
+ return { verdict: reasons.length > 0 ? 'full' : 'candidate', reasons };
186
+ }
187
+ //# sourceMappingURL=prescreen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prescreen.js","sourceRoot":"","sources":["../src/prescreen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared TTL math for the CLI's cache-with-expiry files — `cache-resolver.ts`'s
3
+ * dossier resolution cache and `engine-version.ts`'s npm-latest cache both
4
+ * need "read a configured TTL in seconds, with a default" and "is this
5
+ * timestamp still within that TTL" (#537 review — the two files had
6
+ * near-line-for-line copies of both).
7
+ *
8
+ * File I/O itself stays per-module: `cache-resolver.ts`'s resolution files
9
+ * need org-scoped-path safety and symlink-swap defense that
10
+ * `engine-version.ts`'s single fixed-path cache file does not, so forcing a
11
+ * shared read/write would either under-harden one or over-complicate the
12
+ * other. Only the TTL math — which has no such asymmetry — is shared here.
13
+ */
14
+ /** Read a `cache.*` TTL config key (seconds), falling back to `defaultSeconds` when unset/invalid. */
15
+ export declare function getConfiguredTtlSeconds(configKey: string, defaultSeconds: number): number;
16
+ /** Whether an ISO timestamp is still within `ttlSeconds` of now (never negative-age). */
17
+ export declare function isWithinTtl(timestampIso: string, ttlSeconds: number): boolean;
18
+ //# sourceMappingURL=ttl-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ttl-cache.d.ts","sourceRoot":"","sources":["../src/ttl-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,sGAAsG;AACtG,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAIzF;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAG7E"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Shared TTL math for the CLI's cache-with-expiry files — `cache-resolver.ts`'s
4
+ * dossier resolution cache and `engine-version.ts`'s npm-latest cache both
5
+ * need "read a configured TTL in seconds, with a default" and "is this
6
+ * timestamp still within that TTL" (#537 review — the two files had
7
+ * near-line-for-line copies of both).
8
+ *
9
+ * File I/O itself stays per-module: `cache-resolver.ts`'s resolution files
10
+ * need org-scoped-path safety and symlink-swap defense that
11
+ * `engine-version.ts`'s single fixed-path cache file does not, so forcing a
12
+ * shared read/write would either under-harden one or over-complicate the
13
+ * other. Only the TTL math — which has no such asymmetry — is shared here.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.getConfiguredTtlSeconds = getConfiguredTtlSeconds;
17
+ exports.isWithinTtl = isWithinTtl;
18
+ const config_1 = require("./config");
19
+ /** Read a `cache.*` TTL config key (seconds), falling back to `defaultSeconds` when unset/invalid. */
20
+ function getConfiguredTtlSeconds(configKey, defaultSeconds) {
21
+ const raw = (0, config_1.getConfig)(configKey);
22
+ if (typeof raw === 'number' && Number.isFinite(raw) && raw >= 0)
23
+ return raw;
24
+ return defaultSeconds;
25
+ }
26
+ /** Whether an ISO timestamp is still within `ttlSeconds` of now (never negative-age). */
27
+ function isWithinTtl(timestampIso, ttlSeconds) {
28
+ const ageMs = Date.now() - new Date(timestampIso).getTime();
29
+ return ageMs >= 0 && ageMs < ttlSeconds * 1000;
30
+ }
31
+ //# sourceMappingURL=ttl-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ttl-cache.js","sourceRoot":"","sources":["../src/ttl-cache.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAKH,0DAIC;AAGD,kCAGC;AAbD,qCAAqC;AAErC,sGAAsG;AACtG,SAAgB,uBAAuB,CAAC,SAAiB,EAAE,cAAsB;IAC/E,MAAM,GAAG,GAAG,IAAA,kBAAS,EAAC,SAAS,CAAC,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5E,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,yFAAyF;AACzF,SAAgB,WAAW,CAAC,YAAoB,EAAE,UAAkB;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC;IAC5D,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,GAAG,UAAU,GAAG,IAAI,CAAC;AACjD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-dossier/cli",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Create, verify, sign, publish, and run dossiers — portable, signed, versioned skills for any LLM tool. The dossier ⇄ Claude Code skill bridge (install-skill / skill-export).",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@ai-dossier/core": "^1.5.0",
57
- "@ai-dossier/sched": "^0.12.0",
57
+ "@ai-dossier/sched": "^0.13.0",
58
58
  "commander": "^12.1.0",
59
59
  "yaml": "^2.8.3"
60
60
  },