@gobing-ai/knowledge-kit 0.0.12 → 0.0.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 +1 -1
- package/dist/index.js +120 -26
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +22187 -0
- package/plugins/generations/content-gen/plugin.json +1 -1
- package/plugins/generations/core-facts-gen/dist/index.js +22068 -0
- package/plugins/generations/core-facts-gen/plugin.json +1 -1
- package/plugins/generations/daily-article-gen/dist/index.js +22050 -0
- package/plugins/generations/daily-article-gen/plugin.json +1 -1
- package/plugins/generations/daily-article-gen/src/index.ts +13 -1
- package/plugins/generations/dailynews-gen/dist/index.js +22344 -0
- package/plugins/generations/dailynews-gen/plugin.json +1 -1
- package/plugins/generations/episode-plan-gen/dist/index.js +22503 -0
- package/plugins/generations/episode-plan-gen/plugin.json +1 -1
- package/plugins/generations/episode-plan-gen/src/index.ts +11 -0
- package/plugins/generations/image-gen/config.example.yaml +75 -0
- package/plugins/generations/image-gen/dist/index.js +24862 -0
- package/plugins/generations/image-gen/package.json +17 -0
- package/plugins/generations/image-gen/plugin.json +7 -0
- package/plugins/generations/image-gen/presets/formats/cover.yaml +57 -0
- package/plugins/generations/image-gen/presets/formats/free.yaml +46 -0
- package/plugins/generations/image-gen/presets/formats/illustration.yaml +48 -0
- package/plugins/generations/image-gen/presets/styles/clean-webapp-ui.yaml +28 -0
- package/plugins/generations/image-gen/presets/styles/cute.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/editorial.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/fresh.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/minimalist.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/photorealistic.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/sketch.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/technical-diagram.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/vibrant.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/warm.yaml +3 -0
- package/plugins/generations/image-gen/src/bytes.ts +19 -0
- package/plugins/generations/image-gen/src/index.ts +319 -0
- package/plugins/generations/image-gen/src/job.ts +143 -0
- package/plugins/generations/image-gen/src/paths.ts +31 -0
- package/plugins/generations/image-gen/src/presets.ts +344 -0
- package/plugins/generations/image-gen/src/providers/agnes.ts +110 -0
- package/plugins/generations/image-gen/src/providers/azure.ts +153 -0
- package/plugins/generations/image-gen/src/providers/codex-cli.ts +170 -0
- package/plugins/generations/image-gen/src/providers/dashscope.ts +485 -0
- package/plugins/generations/image-gen/src/providers/google.ts +268 -0
- package/plugins/generations/image-gen/src/providers/huggingface.ts +59 -0
- package/plugins/generations/image-gen/src/providers/jimeng.ts +259 -0
- package/plugins/generations/image-gen/src/providers/minimax.ts +171 -0
- package/plugins/generations/image-gen/src/providers/openai.ts +319 -0
- package/plugins/generations/image-gen/src/providers/openrouter.ts +257 -0
- package/plugins/generations/image-gen/src/providers/refs.ts +24 -0
- package/plugins/generations/image-gen/src/providers/replicate.ts +279 -0
- package/plugins/generations/image-gen/src/providers/seedream.ts +128 -0
- package/plugins/generations/image-gen/src/providers/types.ts +286 -0
- package/plugins/generations/image-gen/src/providers/zai.ts +237 -0
- package/plugins/generations/image-gen/tsconfig.json +8 -0
- package/plugins/generations/news-report-gen/dist/index.js +22193 -0
- package/plugins/generations/news-report-gen/package.json +17 -0
- package/plugins/generations/news-report-gen/plugin.json +7 -0
- package/plugins/generations/news-report-gen/src/index.ts +308 -0
- package/plugins/generations/news-report-gen/tsconfig.json +4 -0
- package/plugins/generations/omni-voice-gen/Makefile +14 -0
- package/plugins/generations/omni-voice-gen/README.md +112 -0
- package/plugins/generations/omni-voice-gen/bin/omni-voice-gen +2 -0
- package/plugins/generations/omni-voice-gen/dist/omni-voice-gen-prr8skpb. +2 -0
- package/plugins/generations/omni-voice-gen/dist/omni-voice-gen.js +6 -0
- package/plugins/generations/omni-voice-gen/plugin.json +6 -0
- package/plugins/generations/omni-voice-gen/profiles.json +12 -0
- package/plugins/generations/omni-voice-gen/pyproject.toml +25 -0
- package/plugins/generations/omni-voice-gen/scripts/coverage_gate.py +74 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/__init__.py +1 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/__main__.py +39 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/audio.py +190 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/backend.py +150 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/contract.py +76 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/mp3.py +60 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +289 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/profiles.py +100 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/qc.py +234 -0
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +352 -0
- package/plugins/generations/omni-voice-gen/uv.lock +3510 -0
- package/plugins/generations/voice-gen/dist/index.js +23055 -0
- package/plugins/generations/voice-gen/plugin.json +1 -1
- package/plugins/generations/voice-gen/src/index.ts +16 -1
- package/plugins/generations/voice-gen/src/voicebox-client.ts +3 -1
- package/plugins/ingestions/aihot-ingest/dist/index.js +22378 -0
- package/plugins/ingestions/aihot-ingest/plugin.json +1 -1
- package/plugins/ingestions/horizon-ingest/dist/index.js +22125 -0
- package/plugins/ingestions/horizon-ingest/plugin.json +1 -1
- package/plugins/ingestions/karakeep-local/dist/index.js +24204 -0
- package/plugins/ingestions/karakeep-local/plugin.json +1 -1
- package/plugins/ingestions/last30days-ingest/dist/index.js +22070 -0
- package/plugins/ingestions/last30days-ingest/plugin.json +1 -1
- package/plugins/ingestions/web-search/dist/index.js +24399 -0
- package/plugins/ingestions/web-search/plugin.json +1 -1
- package/plugins/kk/commands/image-extract.md +40 -0
- package/plugins/kk/commands/image-generate.md +32 -0
- package/plugins/kk/config.example.yaml +80 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/skills/image-authoring/SKILL.md +257 -0
- package/plugins/kk/skills/image-authoring/references/format-drafting.md +57 -0
- package/plugins/kk/skills/image-authoring/references/illustration-positions.md +87 -0
- package/plugins/kk/skills/image-authoring/references/migrating-from-wt.md +31 -0
- package/plugins/kk/skills/image-authoring/references/providers.md +52 -0
- package/plugins/kk/skills/image-authoring/references/style-extraction.md +139 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +130 -30
- package/plugins/publishings/emdash-pub/dist/index.js +22263 -0
- package/plugins/publishings/emdash-pub/plugin.json +1 -1
- package/plugins/publishings/podcast-pub/dist/index.js +22650 -0
- package/plugins/publishings/podcast-pub/plugin.json +8 -2
- package/plugins/publishings/podcast-pub/src/index.ts +18 -2
- package/plugins/publishings/podcast-pub/src/show-notes.ts +56 -9
- package/plugins/publishings/qiita-pub/dist/index.js +22101 -0
- package/plugins/publishings/qiita-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/dist/index.js +22323 -0
- package/plugins/publishings/surfdash-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/src/index.ts +109 -9
- package/plugins/publishings/zenn-pub/dist/index.js +22142 -0
- package/plugins/publishings/zenn-pub/plugin.json +1 -1
- package/plugins/sp/scripts/batch-preflight.mjs +346 -0
- package/plugins/sp/scripts/batch-preflight.ts +459 -0
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +615 -0
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +846 -0
- package/plugins/sp/scripts/daily-summary/logger.ts +28 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +223 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +367 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +132 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +169 -0
- package/plugins/sp/scripts/feature-dev-precheck.mjs +171 -0
- package/plugins/sp/scripts/feature-dev-precheck.ts +238 -0
- package/plugins/sp/scripts/feature-sync-bounded.mjs +285 -0
- package/plugins/sp/scripts/feature-sync-bounded.ts +478 -0
- package/plugins/sp/scripts/history-anatomy-cache.mjs +902 -0
- package/plugins/sp/scripts/history-anatomy-cache.ts +1028 -0
- package/plugins/sp/scripts/idea-handoff.mjs +22 -0
- package/plugins/sp/scripts/idea-handoff.ts +44 -0
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +185 -0
- package/plugins/sp/scripts/inline-run-setup.ts +198 -0
- package/plugins/sp/scripts/pr-reviewing.mjs +769 -0
- package/plugins/sp/scripts/pr-reviewing.ts +925 -0
- package/plugins/sp/scripts/quality-gate.mjs +179 -0
- package/plugins/sp/scripts/quality-gate.ts +217 -0
- package/plugins/sp/scripts/script-contract-check.ts +319 -0
- package/plugins/sp/scripts/stage-registry-adapter.ts +1533 -0
- package/plugins/sp/scripts/surface-drift-inventory.ts +929 -0
- package/plugins/sp/scripts/task-evidence-precheck.ts +181 -0
- package/plugins/sp/scripts/task-size-precheck.ts +175 -0
- package/plugins/sp/scripts/transition-shim-check.ts +238 -0
- package/plugins/sp/scripts/validate-commands.ts +689 -0
- package/plugins/sp/scripts/validate-flag-contracts.ts +878 -0
- package/plugins/sp/scripts/verify-answer-lint.ts +530 -0
- package/plugins/sp/scripts/workflow-step-profile.mjs +316 -0
- package/plugins/sp/scripts/workflow-step-profile.ts +456 -0
- package/plugins/sp/scripts/wrapup-steps.mjs +373 -0
- package/plugins/sp/scripts/wrapup-steps.ts +466 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* batch-preflight — pure TABLE A STOP evaluation for sp:super-planner (task 0279).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors routing-table.md TABLE A hard-STOP preconditions so the batch driver can
|
|
5
|
+
* skip doomed pipeline launches without spawning a Skill subprocess. Happy-path
|
|
6
|
+
* statuses still return `action: 'run'` — the caller launches task-pipeline.yaml
|
|
7
|
+
* verbatim (preflight is readiness only, never a pipeline substitute).
|
|
8
|
+
*
|
|
9
|
+
* Recovery hints (one-shot after FAIL) map stuck statuses to a single /sp:dev-* hop
|
|
10
|
+
* per TABLE A; the batch driver prints or dispatches at most once per WBS.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type TaskStatus = 'backlog' | 'todo' | 'wip' | 'testing' | 'blocked' | 'done' | 'cancelled' | string;
|
|
14
|
+
|
|
15
|
+
export interface PreflightInput {
|
|
16
|
+
wbs: string;
|
|
17
|
+
status: TaskStatus;
|
|
18
|
+
/** Frontmatter dependencies[] WBS list. */
|
|
19
|
+
dependencies: string[];
|
|
20
|
+
/** Status of each dependency WBS (from spur task show). Missing → treated as unmet. */
|
|
21
|
+
depStatuses: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type PreflightResult =
|
|
25
|
+
| { action: 'run'; code?: string; reason?: string }
|
|
26
|
+
| { action: 'skip'; code: string; reason: string; unmetDeps?: string[] };
|
|
27
|
+
|
|
28
|
+
// ── Command-aware quick readiness (task 0814 R2) ──────────────────────────────
|
|
29
|
+
// Read-only admission decision for the requested dev operation. Distinguishes
|
|
30
|
+
// runnable / needs-refinement / blocked / skipped / invalid outcomes without
|
|
31
|
+
// LLM dispatch, full tests/lint, live-data probes, feature mutation, or
|
|
32
|
+
// corpus-wide relational checking. Refinement gaps are work to do, not errors.
|
|
33
|
+
|
|
34
|
+
export type ReadinessOperation = 'run' | 'refine' | 'verify';
|
|
35
|
+
|
|
36
|
+
export interface QuickReadinessInput {
|
|
37
|
+
wbs: string;
|
|
38
|
+
status: TaskStatus;
|
|
39
|
+
operation: ReadinessOperation;
|
|
40
|
+
/** Frontmatter dependencies[] WBS list (run only). */
|
|
41
|
+
dependencies?: string[];
|
|
42
|
+
/** Status of each dependency WBS; missing → treated as unmet (run only). */
|
|
43
|
+
depStatuses?: Record<string, string>;
|
|
44
|
+
/** Size of the status-filtered candidate set after the selector resolved; 0 = empty set. */
|
|
45
|
+
filteredCount?: number;
|
|
46
|
+
/** Required planning sections for this variant+status; empty = not applicable. */
|
|
47
|
+
requiredSections?: string[];
|
|
48
|
+
/** Sections that are actually present (non-placeholder) in the task. */
|
|
49
|
+
presentSections?: string[];
|
|
50
|
+
/** L1/L2/L3 content-policy findings keyed by section; empty = clean. */
|
|
51
|
+
sectionFindings?: Record<string, string>;
|
|
52
|
+
/** Verify only: re-verification semantics (--force) — never a dirty-tree bypass. */
|
|
53
|
+
force?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type QuickReadinessResult =
|
|
57
|
+
| { action: 'runnable'; code: string; reason: string }
|
|
58
|
+
| { action: 'needs-refinement'; code: string; reason: string; gaps: string[] }
|
|
59
|
+
| { action: 'blocked'; code: string; reason: string; unmetDeps?: string[] }
|
|
60
|
+
| { action: 'skipped'; code: string; reason: string }
|
|
61
|
+
| { action: 'invalid'; code: string; reason: string };
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate quick readiness for a requested dev operation (0814 R2). Read-only:
|
|
65
|
+
* no model, no full tests/lint, no live-data probe, no feature mutation, no
|
|
66
|
+
* corpus-wide relational check. An empty status-filtered set is `skipped`
|
|
67
|
+
* (mirrors the zero-task rule), never an error. Refinement gaps under `refine`
|
|
68
|
+
* are work to do, so they do not block; under `run` they are `needs-refinement`.
|
|
69
|
+
*/
|
|
70
|
+
export function quickReadiness(input: QuickReadinessInput): QuickReadinessResult {
|
|
71
|
+
const status = (input.status ?? '').toLowerCase();
|
|
72
|
+
const operation = input.operation;
|
|
73
|
+
|
|
74
|
+
if (operation !== 'run' && operation !== 'refine' && operation !== 'verify') {
|
|
75
|
+
return {
|
|
76
|
+
action: 'invalid',
|
|
77
|
+
code: 'IV',
|
|
78
|
+
reason: `quick-readiness: unknown operation '${operation}' (${input.wbs})`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (input.filteredCount !== undefined) {
|
|
83
|
+
if (input.filteredCount < 0) {
|
|
84
|
+
return {
|
|
85
|
+
action: 'invalid',
|
|
86
|
+
code: 'IV',
|
|
87
|
+
reason: `quick-readiness: invalid negative filtered count '${input.filteredCount}' (${input.wbs})`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (input.filteredCount === 0) {
|
|
91
|
+
return {
|
|
92
|
+
action: 'skipped',
|
|
93
|
+
code: 'EMPTY',
|
|
94
|
+
reason: `quick-readiness: empty status-filtered set — nothing to ${operation} (${input.wbs})`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (status === 'cancelled' || status === 'done') {
|
|
100
|
+
// verify --force re-verification (R2 AC): an already-verified terminal task
|
|
101
|
+
// is re-checked, not skipped — but force never bypasses a dirty tree or the
|
|
102
|
+
// owning gates; it only re-admits a terminal task for re-verification.
|
|
103
|
+
if (operation === 'verify' && input.force === true) {
|
|
104
|
+
return {
|
|
105
|
+
action: 'runnable',
|
|
106
|
+
code: 'FORCE',
|
|
107
|
+
reason: `quick-readiness: verify --force re-verification of ${status} task ${input.wbs}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
action: 'skipped',
|
|
112
|
+
code: status === 'done' ? 'DONE' : 'CANCELLED',
|
|
113
|
+
reason: `quick-readiness: already ${status} — no ${operation} hop (${input.wbs})`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (status === 'blocked') {
|
|
118
|
+
return {
|
|
119
|
+
action: 'blocked',
|
|
120
|
+
code: 'BLK',
|
|
121
|
+
reason: `quick-readiness: blocked — human/handover first (${input.wbs})`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Unmet out-of-set dependency is a block for the operation that needs it.
|
|
126
|
+
if (operation === 'run' && input.dependencies && input.dependencies.length > 0) {
|
|
127
|
+
const unmet = input.dependencies.filter((d) => (input.depStatuses?.[d] ?? 'missing').toLowerCase() !== 'done');
|
|
128
|
+
if (unmet.length > 0) {
|
|
129
|
+
return {
|
|
130
|
+
action: 'blocked',
|
|
131
|
+
code: 'DEP',
|
|
132
|
+
reason: `quick-readiness: unmet deps — ${unmet.join(', ')} (${input.wbs})`,
|
|
133
|
+
unmetDeps: unmet,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const required = input.requiredSections ?? [];
|
|
139
|
+
const present = input.presentSections ?? [];
|
|
140
|
+
// A required section is a gap when it is absent from the present-set OR carries a
|
|
141
|
+
// content-policy finding (the caller-supplied `sectionFindings` from the matrix /
|
|
142
|
+
// `TaskCheckService.checkContentPolicy`). This lets the function detect a gap itself
|
|
143
|
+
// rather than depending on the caller to pre-enumerate every missing section.
|
|
144
|
+
const gaps = required.filter((s) => {
|
|
145
|
+
const finding = input.sectionFindings?.[s];
|
|
146
|
+
return !present.includes(s) || (finding !== undefined && finding !== '');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// refine: missing/incomplete planning sections are the work, not a failure.
|
|
150
|
+
if (operation === 'refine') {
|
|
151
|
+
if (status !== 'backlog' && status !== 'todo') {
|
|
152
|
+
return {
|
|
153
|
+
action: 'skipped',
|
|
154
|
+
code: 'NONPLAN',
|
|
155
|
+
reason: `quick-readiness: refine targets backlog/todo only, not '${status}' (${input.wbs})`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
action: 'runnable',
|
|
160
|
+
code: 'OK',
|
|
161
|
+
reason: `quick-readiness: refine ready for ${input.wbs} (${gaps.length} planning gap(s) to fill)`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (operation === 'verify') {
|
|
166
|
+
if (status !== 'testing' && status !== 'wip') {
|
|
167
|
+
return {
|
|
168
|
+
action: 'invalid',
|
|
169
|
+
code: 'NOVERIFY',
|
|
170
|
+
reason: `quick-readiness: verify needs testing/wip, not '${status}' (${input.wbs})`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
action: 'runnable',
|
|
175
|
+
code: 'OK',
|
|
176
|
+
reason: `quick-readiness: verify ready for ${input.wbs}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// run: implementation admission. Eligible statuses are todo/wip/testing;
|
|
181
|
+
// a backlog task needs the chain's auto-promotion first (step 0).
|
|
182
|
+
if (status !== 'todo' && status !== 'wip' && status !== 'testing') {
|
|
183
|
+
return {
|
|
184
|
+
action: 'invalid',
|
|
185
|
+
code: 'NORUN',
|
|
186
|
+
reason: `quick-readiness: run needs todo/wip/testing, not '${status}' (${input.wbs})`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (gaps.length > 0) {
|
|
190
|
+
return {
|
|
191
|
+
action: 'needs-refinement',
|
|
192
|
+
code: 'REFINE',
|
|
193
|
+
reason: `quick-readiness: implementation sections incomplete (${input.wbs})`,
|
|
194
|
+
gaps,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
action: 'runnable',
|
|
199
|
+
code: 'OK',
|
|
200
|
+
reason: `quick-readiness: run ready for ${input.wbs}`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Evaluate whether the batch should launch task-pipeline.yaml for this WBS.
|
|
206
|
+
* STOP codes align with routing-table TABLE A row ids (A2, A7, A8, A9).
|
|
207
|
+
*/
|
|
208
|
+
export function preflightTask(input: PreflightInput): PreflightResult {
|
|
209
|
+
const status = (input.status ?? '').toLowerCase();
|
|
210
|
+
const deps = input.dependencies ?? [];
|
|
211
|
+
const depStatuses = input.depStatuses ?? {};
|
|
212
|
+
|
|
213
|
+
if (status === 'cancelled') {
|
|
214
|
+
return {
|
|
215
|
+
action: 'skip',
|
|
216
|
+
code: 'A9',
|
|
217
|
+
reason: `dev-next: cancelled — nothing to advance (${input.wbs})`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (status === 'done') {
|
|
222
|
+
return {
|
|
223
|
+
action: 'skip',
|
|
224
|
+
code: 'A8',
|
|
225
|
+
reason: `dev-next: already done — batch does not auto-wrap (${input.wbs})`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (status === 'blocked') {
|
|
230
|
+
return {
|
|
231
|
+
action: 'skip',
|
|
232
|
+
code: 'A7',
|
|
233
|
+
reason: `dev-next: blocked — do not launch pipeline; human/handover first (${input.wbs})`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// A2: todo/backlog with any dependency not done (mirrors TABLE A2 / ready filter).
|
|
238
|
+
if (status === 'todo' || status === 'backlog') {
|
|
239
|
+
const unmet = deps.filter((d) => {
|
|
240
|
+
const st = (depStatuses[d] ?? 'missing').toLowerCase();
|
|
241
|
+
return st !== 'done';
|
|
242
|
+
});
|
|
243
|
+
if (unmet.length > 0) {
|
|
244
|
+
return {
|
|
245
|
+
action: 'skip',
|
|
246
|
+
code: 'A2',
|
|
247
|
+
reason: `dev-next: blocked by deps — unmet: ${unmet.join(', ')} (${input.wbs})`,
|
|
248
|
+
unmetDeps: unmet,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Ready for pipeline: backlog/todo (deps ok), wip, testing.
|
|
254
|
+
return { action: 'run', code: 'OK', reason: `preflight clear — launch task-pipeline for ${input.wbs}` };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* One-shot recovery hint after a non-PASS pipeline or stuck status (TABLE A primary hop).
|
|
259
|
+
* Caller substitutes the real WBS; never loop this — at most one hop per WBS per batch.
|
|
260
|
+
*/
|
|
261
|
+
export function recoveryHint(status: TaskStatus, wbs: string): { command: string; code: string } | null {
|
|
262
|
+
const st = (status ?? '').toLowerCase();
|
|
263
|
+
switch (st) {
|
|
264
|
+
case 'backlog':
|
|
265
|
+
return { code: 'A1', command: `/sp:dev-refine ${wbs} --auto --next` };
|
|
266
|
+
case 'todo':
|
|
267
|
+
return { code: 'A3', command: `/sp:dev-run ${wbs} --auto --next` };
|
|
268
|
+
case 'wip':
|
|
269
|
+
return { code: 'A5', command: `/sp:dev-run ${wbs} --mode implement --auto --next` };
|
|
270
|
+
case 'testing':
|
|
271
|
+
return { code: 'A6', command: `/sp:dev-verify ${wbs} --auto --next` };
|
|
272
|
+
case 'blocked':
|
|
273
|
+
return {
|
|
274
|
+
code: 'A7',
|
|
275
|
+
command: `/sp:dev-handover "blocked task ${wbs} — see Notes/History"`,
|
|
276
|
+
};
|
|
277
|
+
default:
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ── CLI for agents / dogfood (optional) ──────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
export interface PreflightCliArgs {
|
|
285
|
+
status: string | null;
|
|
286
|
+
deps: string[];
|
|
287
|
+
depStatuses: Record<string, string>;
|
|
288
|
+
wbs: string;
|
|
289
|
+
recovery: boolean;
|
|
290
|
+
help: boolean;
|
|
291
|
+
json: boolean;
|
|
292
|
+
/** Quick-readiness operation (run|refine|verify); when set, run quickReadiness. */
|
|
293
|
+
operation: ReadinessOperation | null;
|
|
294
|
+
/** Verify-only re-verification (R2 AC). */
|
|
295
|
+
force: boolean;
|
|
296
|
+
/** Status-filtered candidate set size; 0 = empty set. */
|
|
297
|
+
filteredCount: number | null;
|
|
298
|
+
/** Required planning sections (matrix-selected). */
|
|
299
|
+
requiredSections: string[];
|
|
300
|
+
/** Sections actually present in the task. */
|
|
301
|
+
presentSections: string[];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function parsePreflightCliArgs(argv: string[]): PreflightCliArgs {
|
|
305
|
+
let status: string | null = null;
|
|
306
|
+
let deps: string[] = [];
|
|
307
|
+
const depStatuses: Record<string, string> = {};
|
|
308
|
+
let wbs = '0000';
|
|
309
|
+
let recovery = false;
|
|
310
|
+
let help = false;
|
|
311
|
+
let json = false;
|
|
312
|
+
let operation: ReadinessOperation | null = null;
|
|
313
|
+
let force = false;
|
|
314
|
+
let filteredCount: number | null = null;
|
|
315
|
+
let requiredSections: string[] = [];
|
|
316
|
+
let presentSections: string[] = [];
|
|
317
|
+
|
|
318
|
+
for (let i = 0; i < argv.length; i++) {
|
|
319
|
+
const a = argv[i];
|
|
320
|
+
if (a === '--help' || a === '-h') help = true;
|
|
321
|
+
else if (a === '--json') json = true;
|
|
322
|
+
else if (a === '--recovery') recovery = true;
|
|
323
|
+
else if (a === '--force') force = true;
|
|
324
|
+
else if (a === '--operation') {
|
|
325
|
+
const v = argv[++i] ?? '';
|
|
326
|
+
operation = v === 'run' || v === 'refine' || v === 'verify' ? v : null;
|
|
327
|
+
} else if (a === '--filtered-count') {
|
|
328
|
+
const v = Number(argv[++i]);
|
|
329
|
+
filteredCount = Number.isFinite(v) ? v : null;
|
|
330
|
+
} else if (a === '--required-sections') {
|
|
331
|
+
const raw = argv[++i] ?? '';
|
|
332
|
+
requiredSections =
|
|
333
|
+
raw.length === 0
|
|
334
|
+
? []
|
|
335
|
+
: raw
|
|
336
|
+
.split(',')
|
|
337
|
+
.map((s) => s.trim())
|
|
338
|
+
.filter(Boolean);
|
|
339
|
+
} else if (a === '--present-sections') {
|
|
340
|
+
const raw = argv[++i] ?? '';
|
|
341
|
+
presentSections =
|
|
342
|
+
raw.length === 0
|
|
343
|
+
? []
|
|
344
|
+
: raw
|
|
345
|
+
.split(',')
|
|
346
|
+
.map((s) => s.trim())
|
|
347
|
+
.filter(Boolean);
|
|
348
|
+
} else if (a === '--wbs') wbs = argv[++i] ?? wbs;
|
|
349
|
+
else if (a === '--status') status = argv[++i] ?? null;
|
|
350
|
+
else if (a === '--deps') {
|
|
351
|
+
const raw = argv[++i] ?? '';
|
|
352
|
+
deps =
|
|
353
|
+
raw.length === 0
|
|
354
|
+
? []
|
|
355
|
+
: raw
|
|
356
|
+
.split(',')
|
|
357
|
+
.map((s) => s.trim())
|
|
358
|
+
.filter(Boolean);
|
|
359
|
+
} else if (a === '--dep-status') {
|
|
360
|
+
// format: 0275:done,0276:todo
|
|
361
|
+
const raw = argv[++i] ?? '';
|
|
362
|
+
for (const part of raw.split(',')) {
|
|
363
|
+
const [k, v] = part.split(':');
|
|
364
|
+
if (k && v) depStatuses[k.trim()] = v.trim();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
status,
|
|
370
|
+
deps,
|
|
371
|
+
depStatuses,
|
|
372
|
+
wbs,
|
|
373
|
+
recovery,
|
|
374
|
+
help,
|
|
375
|
+
json,
|
|
376
|
+
operation,
|
|
377
|
+
force,
|
|
378
|
+
filteredCount,
|
|
379
|
+
requiredSections,
|
|
380
|
+
presentSections,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export const PREFLIGHT_CLI_USAGE = `Usage:
|
|
385
|
+
bun plugins/sp/scripts/batch-preflight.ts --wbs <wbs> --status <status> \\
|
|
386
|
+
[--deps 0275,0276] [--dep-status 0275:done,0276:todo] [--recovery] [--json]
|
|
387
|
+
bun plugins/sp/scripts/batch-preflight.ts --operation <run|refine|verify> --wbs <wbs> --status <status> \
|
|
388
|
+
[--filtered-count <n>] [--required-sections A,B] [--present-sections A,B] [--force] [--json]
|
|
389
|
+
|
|
390
|
+
Exit: 0 = run (or recovery hint printed); 2 = skip; 1 = usage.`;
|
|
391
|
+
|
|
392
|
+
export function runPreflightCli(argv: string[]): { exitCode: number; stdout: string; stderr: string } {
|
|
393
|
+
const args = parsePreflightCliArgs(argv);
|
|
394
|
+
if (args.help) return { exitCode: 0, stdout: '', stderr: PREFLIGHT_CLI_USAGE };
|
|
395
|
+
if (!args.status) return { exitCode: 1, stdout: '', stderr: PREFLIGHT_CLI_USAGE };
|
|
396
|
+
|
|
397
|
+
if (args.recovery) {
|
|
398
|
+
const hint = recoveryHint(args.status, args.wbs);
|
|
399
|
+
const body = args.json
|
|
400
|
+
? `${JSON.stringify({ recovery: hint }, null, 2)}\n`
|
|
401
|
+
: hint
|
|
402
|
+
? `${hint.command}\n`
|
|
403
|
+
: 'no recovery hop\n';
|
|
404
|
+
return { exitCode: 0, stdout: body, stderr: '' };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Quick command-aware readiness (0814 R2) — read-only admission decision.
|
|
408
|
+
if (args.operation !== null) {
|
|
409
|
+
const result = quickReadiness({
|
|
410
|
+
wbs: args.wbs,
|
|
411
|
+
status: args.status,
|
|
412
|
+
operation: args.operation,
|
|
413
|
+
dependencies: args.deps,
|
|
414
|
+
depStatuses: args.depStatuses,
|
|
415
|
+
...(args.filteredCount !== null ? { filteredCount: args.filteredCount } : {}),
|
|
416
|
+
...(args.requiredSections.length > 0 ? { requiredSections: args.requiredSections } : {}),
|
|
417
|
+
...(args.presentSections.length > 0 ? { presentSections: args.presentSections } : {}),
|
|
418
|
+
...(args.force ? { force: true } : {}),
|
|
419
|
+
});
|
|
420
|
+
const runnable = result.action === 'runnable' || result.action === 'needs-refinement';
|
|
421
|
+
if (args.json) {
|
|
422
|
+
return { exitCode: runnable ? 0 : 2, stdout: `${JSON.stringify(result, null, 2)}\n`, stderr: '' };
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
exitCode: runnable ? 0 : 2,
|
|
426
|
+
stdout: `${result.action}${result.code ? ` ${result.code}` : ''}: ${result.reason}\n`,
|
|
427
|
+
stderr: '',
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const result = preflightTask({
|
|
432
|
+
wbs: args.wbs,
|
|
433
|
+
status: args.status,
|
|
434
|
+
dependencies: args.deps,
|
|
435
|
+
depStatuses: args.depStatuses,
|
|
436
|
+
});
|
|
437
|
+
if (args.json) {
|
|
438
|
+
return {
|
|
439
|
+
exitCode: result.action === 'run' ? 0 : 2,
|
|
440
|
+
stdout: `${JSON.stringify(result, null, 2)}\n`,
|
|
441
|
+
stderr: '',
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (result.action === 'run') {
|
|
445
|
+
return { exitCode: 0, stdout: `run: ${result.reason ?? 'ok'}\n`, stderr: '' };
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
exitCode: 2,
|
|
449
|
+
stdout: `skip ${result.code}: ${result.reason}\n`,
|
|
450
|
+
stderr: '',
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (import.meta.main) {
|
|
455
|
+
const { exitCode, stdout, stderr } = runPreflightCli(process.argv.slice(2));
|
|
456
|
+
if (stdout) process.stdout.write(stdout);
|
|
457
|
+
if (stderr) process.stderr.write(`${stderr}\n`);
|
|
458
|
+
process.exit(exitCode);
|
|
459
|
+
}
|