@tiangong-ai/cli 0.0.34 → 0.0.35
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/AGENTS.md +4 -2
- package/README.md +63 -0
- package/dist/research/orchestration.js +366 -9
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/workspace/input-plan.js +27 -1
- package/dist/research/workspace/input-plan.js.map +1 -1
- package/dist/research/workspace/projects.d.ts +6 -3
- package/dist/research/workspace/projects.js +21 -2
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/publication-workflow.d.ts +146 -0
- package/dist/research/workspace/publication-workflow.js +992 -0
- package/dist/research/workspace/publication-workflow.js.map +1 -0
- package/dist/research/workspace/publication.d.ts +110 -0
- package/dist/research/workspace/publication.js +246 -0
- package/dist/research/workspace/publication.js.map +1 -0
- package/dist/research/workspace/research-policy-wizard.d.ts +40 -0
- package/dist/research/workspace/research-policy-wizard.js +167 -0
- package/dist/research/workspace/research-policy-wizard.js.map +1 -0
- package/dist/research/workspace/research-policy.d.ts +70 -0
- package/dist/research/workspace/research-policy.js +886 -0
- package/dist/research/workspace/research-policy.js.map +1 -0
- package/dist/research/workspace/runtime.d.ts +17 -0
- package/dist/research/workspace/runtime.js +81 -2
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/setup-catalog.js +2 -2
- package/dist/research/workspace/setup-wizard.d.ts +18 -1
- package/dist/research/workspace/setup-wizard.js +1 -1
- package/dist/research/workspace/setup-wizard.js.map +1 -1
- package/dist/research/workspace/setup.d.ts +1 -0
- package/dist/research/workspace/setup.js +55 -0
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/types.d.ts +31 -0
- package/package.json +3 -2
package/AGENTS.md
CHANGED
|
@@ -15,8 +15,8 @@ checkPaths:
|
|
|
15
15
|
- .docpact/config.yaml
|
|
16
16
|
- docs/agents/**
|
|
17
17
|
- src/**
|
|
18
|
-
lastReviewedAt: 2026-08-
|
|
19
|
-
lastReviewedCommit:
|
|
18
|
+
lastReviewedAt: 2026-08-13
|
|
19
|
+
lastReviewedCommit: df263405bccb21cd5838d92d82dccf80ca01acb1
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
# Tiangong AI CLI Contract
|
|
@@ -47,6 +47,8 @@ This repository owns the Tiangong AI command-line interface.
|
|
|
47
47
|
- `tiangong-ai kb status`
|
|
48
48
|
- `tiangong-ai research context`
|
|
49
49
|
- `tiangong-ai research setup`
|
|
50
|
+
- `tiangong-ai research policy`
|
|
51
|
+
- `tiangong-ai research publication`
|
|
50
52
|
- `tiangong-ai research workspace`
|
|
51
53
|
- `tiangong-ai research capability`
|
|
52
54
|
- `tiangong-ai research project`
|
package/README.md
CHANGED
|
@@ -269,6 +269,69 @@ regular non-symlink `runtime-lock.json` exact stable CLI version. Setup and
|
|
|
269
269
|
release CI reject a missing resolver or any stale exact CLI version in the
|
|
270
270
|
orchestrator's `SKILL.md` or `references/*.md`.
|
|
271
271
|
|
|
272
|
+
### Top-journal Research Policy and final publication gate
|
|
273
|
+
|
|
274
|
+
A `top-journal` project starts with a human-reviewed Markdown Policy, not with
|
|
275
|
+
model execution. After project-scoped setup reaches `READY`, use the guided
|
|
276
|
+
Wizard:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
tiangong-ai research policy wizard top-journal-paper \
|
|
280
|
+
--workspace /absolute/path/to/workspace
|
|
281
|
+
tiangong-ai research policy status top-journal-paper \
|
|
282
|
+
--workspace /absolute/path/to/workspace --json
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The Wizard resolves only the verified project-installed
|
|
286
|
+
`tiangong-auto-research` tree. It copies a baseline plus one article type,
|
|
287
|
+
field, journal class, project brief, and four reviewer rubrics. Generic defaults
|
|
288
|
+
are clearly reported and require a separate acknowledgement. An exact-journal
|
|
289
|
+
Policy additionally requires a current official HTTPS guideline URL, retrieval
|
|
290
|
+
date, and substantive human content for all journal-specific sections. Approval
|
|
291
|
+
binds the manifest and every document by SHA-256; edits, manifest tampering, or
|
|
292
|
+
expiry block preflight and all later stages until the Policy is reviewed and
|
|
293
|
+
approved again.
|
|
294
|
+
|
|
295
|
+
Use the Policy project ID when admitting the research project:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
tiangong-ai research project preflight \
|
|
299
|
+
--question "A specific, testable research question" \
|
|
300
|
+
--goal top-journal --policy-project top-journal-paper \
|
|
301
|
+
--requirements /absolute/path/to/evidence-requirements.json \
|
|
302
|
+
--workspace /absolute/path/to/workspace --json
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The base evidence lifecycle remains
|
|
306
|
+
`discover -> acquire -> analyze -> synthesize -> review -> close`, authored in
|
|
307
|
+
the current interactive Codex or Claude Code host. After base closure, the
|
|
308
|
+
current native host writes a final manuscript and schema-valid publication
|
|
309
|
+
assessment. `research publication freeze` then content-addresses the Policy,
|
|
310
|
+
evidence snapshot, base outputs, manuscript, assessment, and supplements.
|
|
311
|
+
Exactly four fresh independent sessions review that frozen generation:
|
|
312
|
+
evidence, methods/reproducibility, domain/novelty, and journal-editor. A revised
|
|
313
|
+
manuscript invalidates prior reviews; reviewer-session reuse is rejected from
|
|
314
|
+
the append-only journal even if mutable cache state is removed. The raw opaque
|
|
315
|
+
producer/reviewer session identifiers are accepted only at the command boundary;
|
|
316
|
+
generation, packet, review, journal, and closure objects persist only their
|
|
317
|
+
SHA-256 bindings.
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
tiangong-ai research schema show publication-assessment --json
|
|
321
|
+
tiangong-ai research publication freeze top-journal-paper \
|
|
322
|
+
--manuscript /absolute/path/to/final-manuscript.md \
|
|
323
|
+
--assessment /absolute/path/to/publication-assessment.json \
|
|
324
|
+
--producer-agent codex --producer-session OPAQUE_NATIVE_SESSION \
|
|
325
|
+
--workspace /absolute/path/to/workspace --json
|
|
326
|
+
tiangong-ai research publication status top-journal-paper \
|
|
327
|
+
--workspace /absolute/path/to/workspace --json
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The CLI returns a mechanically bounded ceiling:
|
|
331
|
+
`top-journal-candidate`, `top-journal-class-ready`, or
|
|
332
|
+
`target-journal-submission-ready`. Evidence and review failures can only lower
|
|
333
|
+
it. None of these states predicts or guarantees editorial acceptance.
|
|
334
|
+
|
|
272
335
|
`research setup status --json` reports credential persistence separately from
|
|
273
336
|
readiness. It also reports the effective exact-npx CLI package/version/root,
|
|
274
337
|
the selected project orchestrator, any temporary recovery shim, ignored global
|
|
@@ -20,6 +20,9 @@ import { recordNativeResearchActivity } from "./workspace/native-activity.js";
|
|
|
20
20
|
import { readAndVerifyProjectInputPlan } from "./workspace/input-plan.js";
|
|
21
21
|
import { addProjectInput, createProjectAddendum, initializeProject, forkProject, listProjects, loadProject, nextReadyPackage, normalizeEvidenceRequirements, refreshProject, retryProjectPackage, setProjectDisposition, } from "./workspace/projects.js";
|
|
22
22
|
import { evaluateProjectPreflight } from "./workspace/preflight.js";
|
|
23
|
+
import { closePublication, freezePublicationManuscript, inspectPublicationStatus, preparePublicationReview, publicationAssessmentSchema, publicationReviewSchema, submitPublicationReview, } from "./workspace/publication-workflow.js";
|
|
24
|
+
import { approveResearchPolicy, initializeResearchPolicy, inspectResearchPolicyCatalog, inspectResearchPolicyStatus, loadApprovedResearchPolicy, } from "./workspace/research-policy.js";
|
|
25
|
+
import { resolveInstalledResearchPolicySource, runInteractiveResearchPolicyWizard, } from "./workspace/research-policy-wizard.js";
|
|
23
26
|
import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeResearchStage, requestResearchHandoff, resolveResearchHandoff, runResearchWorkspace, submitNativeResearchStage, } from "./workspace/runtime.js";
|
|
24
27
|
import { schemaForStage } from "./workspace/schemas.js";
|
|
25
28
|
import { pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
|
|
@@ -35,6 +38,10 @@ export async function runResearchOrchestrationCommand(subcommand, argv, io) {
|
|
|
35
38
|
return runWorkspace(argv, io);
|
|
36
39
|
if (subcommand === "capability")
|
|
37
40
|
return runCapability(argv, io);
|
|
41
|
+
if (subcommand === "policy")
|
|
42
|
+
return runPolicy(argv, io);
|
|
43
|
+
if (subcommand === "publication")
|
|
44
|
+
return runPublication(argv, io);
|
|
38
45
|
if (subcommand === "project")
|
|
39
46
|
return runProject(argv, io);
|
|
40
47
|
if (subcommand === "schema")
|
|
@@ -59,9 +66,21 @@ export function researchOrchestrationHelp() {
|
|
|
59
66
|
tiangong-ai research capability credential set --id <logical-id> --from-env <name> [--workspace <absolute-path>] [--json]
|
|
60
67
|
tiangong-ai research capability lock [--workspace <absolute-path>] [--json]
|
|
61
68
|
tiangong-ai research capability verify [--workspace <absolute-path>] [--json]
|
|
62
|
-
tiangong-ai research
|
|
63
|
-
tiangong-ai research
|
|
64
|
-
tiangong-ai research
|
|
69
|
+
tiangong-ai research policy wizard <project-id> [--workspace <path>] [--json]
|
|
70
|
+
tiangong-ai research policy catalog [--source-root <absolute-installed-skill-root>] [--workspace <path>] [--json]
|
|
71
|
+
tiangong-ai research policy init <project-id> [--source-root <absolute-installed-skill-root>] --article-type <id> --field <id> --journal-class <id> [--include-exact-journal-template] [--workspace <path>] [--json]
|
|
72
|
+
tiangong-ai research policy status <project-id> [--workspace <path>] [--json]
|
|
73
|
+
tiangong-ai research policy validate <project-id> [--workspace <path>] [--json]
|
|
74
|
+
tiangong-ai research policy approve <project-id> --confirm [--acknowledge-defaults] [--workspace <path>] [--json]
|
|
75
|
+
tiangong-ai research policy resolve <project-id> [--workspace <path>] [--json]
|
|
76
|
+
tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --producer-agent codex|claude --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
|
|
77
|
+
tiangong-ai research publication review prepare <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
|
|
78
|
+
tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
|
|
79
|
+
tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
|
|
80
|
+
tiangong-ai research publication close <project-id> [--workspace <path>] [--json]
|
|
81
|
+
tiangong-ai research project init <project-id> --question <question> [--goal evidence-report|top-journal] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
|
|
82
|
+
tiangong-ai research project preflight --question <question> [--goal evidence-report|top-journal] [--policy-project <project-id>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
|
|
83
|
+
tiangong-ai research project input add <project-id> --path <absolute-file> [--role primary|reference|replication] [--trust-status verified-owner-input|unverified-owner-input|reference-only|replication-candidate] [--independently-reproduced] [--workspace <path>] [--json]
|
|
65
84
|
tiangong-ai research project retry <project-id> [--package <package-id>] [--workspace <path>] [--json]
|
|
66
85
|
tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--workspace <path>] [--json]
|
|
67
86
|
tiangong-ai research project addendum <closed-project-id> --to <target-project-id> [--workspace <path>] [--json]
|
|
@@ -78,13 +97,206 @@ export function researchOrchestrationHelp() {
|
|
|
78
97
|
tiangong-ai research project evidence assessment record <project-id> --record <absolute-json> [--workspace <path>] [--json]
|
|
79
98
|
tiangong-ai research project evidence download bind <project-id> --candidate <id> --record <absolute-json> [--workspace <path>] [--json]
|
|
80
99
|
tiangong-ai research project evidence artifact register <project-id> --candidate <id> --path <absolute-file> [--download-binding <id> | --derived-from-artifact <id>] [--media-type <type>] [--source-url <https-url>] [--license <declared-license>] [--license-url <https-url>] [--host-type <type>] [--article-version <version>] [--workspace <path>] [--json]
|
|
81
|
-
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor> [--json]
|
|
100
|
+
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor|publication-assessment|publication-review-evidence|publication-review-methods-reproducibility|publication-review-domain-novelty|publication-review-journal-editor> [--json]
|
|
82
101
|
tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
|
|
83
102
|
tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
|
|
84
103
|
|
|
85
104
|
${researchSetupHelp()}
|
|
86
105
|
`;
|
|
87
106
|
}
|
|
107
|
+
async function runPublication(argv, io) {
|
|
108
|
+
const [action, ...rest] = argv;
|
|
109
|
+
if (!action || action === "--help" || action === "-h")
|
|
110
|
+
return writeHelp(io);
|
|
111
|
+
if (action === "freeze") {
|
|
112
|
+
const args = parseStrictArgs(rest, {
|
|
113
|
+
...WORKSPACE_OPTIONS,
|
|
114
|
+
manuscript: "string",
|
|
115
|
+
assessment: "string",
|
|
116
|
+
supplements: "string",
|
|
117
|
+
"producer-agent": "string",
|
|
118
|
+
"producer-session": "string",
|
|
119
|
+
}, "research publication freeze");
|
|
120
|
+
if (strictBoolean(args, "help"))
|
|
121
|
+
return writeHelp(io);
|
|
122
|
+
const projectId = onePositional(args.positionals, "research publication freeze");
|
|
123
|
+
const manuscriptPath = strictString(args, "manuscript");
|
|
124
|
+
const assessmentPath = strictString(args, "assessment");
|
|
125
|
+
const producerSessionId = strictString(args, "producer-session");
|
|
126
|
+
if (!manuscriptPath || !assessmentPath || !producerSessionId) {
|
|
127
|
+
throw new CliError("research publication freeze requires --manuscript, --assessment, --producer-agent, and --producer-session.", { code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED", exitCode: 2 });
|
|
128
|
+
}
|
|
129
|
+
const root = await workspaceFromArgs(args);
|
|
130
|
+
const supplementsPath = strictString(args, "supplements");
|
|
131
|
+
writeJson(io, await freezePublicationManuscript({
|
|
132
|
+
root,
|
|
133
|
+
projectId,
|
|
134
|
+
manuscriptPath,
|
|
135
|
+
assessmentPath,
|
|
136
|
+
supplementPaths: supplementsPath ? await readAbsolutePathArray(supplementsPath) : [],
|
|
137
|
+
producerAgent: publicationAgent(strictString(args, "producer-agent"), "producer"),
|
|
138
|
+
producerSessionId,
|
|
139
|
+
}), args);
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
if (action === "review") {
|
|
143
|
+
const [reviewAction, ...reviewRest] = rest;
|
|
144
|
+
if (reviewAction === "--help" || reviewAction === "-h")
|
|
145
|
+
return writeHelp(io);
|
|
146
|
+
if (reviewAction === "prepare") {
|
|
147
|
+
const args = parseStrictArgs(reviewRest, {
|
|
148
|
+
...WORKSPACE_OPTIONS,
|
|
149
|
+
role: "string",
|
|
150
|
+
"reviewer-agent": "string",
|
|
151
|
+
"reviewer-session": "string",
|
|
152
|
+
}, "research publication review prepare");
|
|
153
|
+
if (strictBoolean(args, "help"))
|
|
154
|
+
return writeHelp(io);
|
|
155
|
+
const projectId = onePositional(args.positionals, "research publication review prepare");
|
|
156
|
+
const reviewerSessionId = strictString(args, "reviewer-session");
|
|
157
|
+
if (!reviewerSessionId) {
|
|
158
|
+
throw new CliError("publication review prepare requires --reviewer-session.", {
|
|
159
|
+
code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
|
|
160
|
+
exitCode: 2,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const root = await workspaceFromArgs(args);
|
|
164
|
+
writeJson(io, await preparePublicationReview({
|
|
165
|
+
root,
|
|
166
|
+
projectId,
|
|
167
|
+
role: publicationReviewRole(strictString(args, "role")),
|
|
168
|
+
reviewerAgent: publicationAgent(strictString(args, "reviewer-agent"), "reviewer"),
|
|
169
|
+
reviewerSessionId,
|
|
170
|
+
}), args);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
if (reviewAction === "submit") {
|
|
174
|
+
const args = parseStrictArgs(reviewRest, { ...WORKSPACE_OPTIONS, role: "string", review: "string" }, "research publication review submit");
|
|
175
|
+
if (strictBoolean(args, "help"))
|
|
176
|
+
return writeHelp(io);
|
|
177
|
+
const projectId = onePositional(args.positionals, "research publication review submit");
|
|
178
|
+
const reviewPath = strictString(args, "review");
|
|
179
|
+
if (!reviewPath) {
|
|
180
|
+
throw new CliError("publication review submit requires --review.", {
|
|
181
|
+
code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
|
|
182
|
+
exitCode: 2,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const root = await workspaceFromArgs(args);
|
|
186
|
+
writeJson(io, await submitPublicationReview({
|
|
187
|
+
root,
|
|
188
|
+
projectId,
|
|
189
|
+
role: publicationReviewRole(strictString(args, "role")),
|
|
190
|
+
reviewPath,
|
|
191
|
+
}), args);
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
throw unknownAction("research publication review", reviewAction ?? "missing");
|
|
195
|
+
}
|
|
196
|
+
if (action === "status" || action === "close") {
|
|
197
|
+
const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, `research publication ${action}`);
|
|
198
|
+
if (strictBoolean(args, "help"))
|
|
199
|
+
return writeHelp(io);
|
|
200
|
+
const projectId = onePositional(args.positionals, `research publication ${action}`);
|
|
201
|
+
const root = await workspaceFromArgs(args);
|
|
202
|
+
writeJson(io, action === "status"
|
|
203
|
+
? await inspectPublicationStatus(root, projectId)
|
|
204
|
+
: await closePublication(root, projectId), args);
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
throw unknownAction("research publication", action);
|
|
208
|
+
}
|
|
209
|
+
async function runPolicy(argv, io) {
|
|
210
|
+
const [action, ...rest] = argv;
|
|
211
|
+
if (!action || action === "--help" || action === "-h")
|
|
212
|
+
return writeHelp(io);
|
|
213
|
+
if (action === "wizard") {
|
|
214
|
+
const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, "research policy wizard");
|
|
215
|
+
if (strictBoolean(args, "help"))
|
|
216
|
+
return writeHelp(io);
|
|
217
|
+
const projectId = onePositional(args.positionals, "research policy wizard");
|
|
218
|
+
const root = await workspaceFromArgs(args);
|
|
219
|
+
return runInteractiveResearchPolicyWizard({
|
|
220
|
+
root,
|
|
221
|
+
projectId,
|
|
222
|
+
io,
|
|
223
|
+
json: strictBoolean(args, "json"),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (action === "catalog") {
|
|
227
|
+
const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "source-root": "string" }, "research policy catalog");
|
|
228
|
+
if (strictBoolean(args, "help"))
|
|
229
|
+
return writeHelp(io);
|
|
230
|
+
rejectPositionals(args.positionals, "research policy catalog");
|
|
231
|
+
const explicitSourceRoot = strictString(args, "source-root");
|
|
232
|
+
const sourceRoot = explicitSourceRoot ??
|
|
233
|
+
(await resolveInstalledResearchPolicySource(await workspaceFromArgs(args), io.env));
|
|
234
|
+
writeJson(io, await inspectResearchPolicyCatalog(sourceRoot), args);
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
if (action === "init") {
|
|
238
|
+
const args = parseStrictArgs(rest, {
|
|
239
|
+
...WORKSPACE_OPTIONS,
|
|
240
|
+
"source-root": "string",
|
|
241
|
+
"article-type": "string",
|
|
242
|
+
field: "string",
|
|
243
|
+
"journal-class": "string",
|
|
244
|
+
"include-exact-journal-template": "boolean",
|
|
245
|
+
}, "research policy init");
|
|
246
|
+
if (strictBoolean(args, "help"))
|
|
247
|
+
return writeHelp(io);
|
|
248
|
+
const projectId = onePositional(args.positionals, "research policy init");
|
|
249
|
+
const articleType = strictString(args, "article-type");
|
|
250
|
+
const field = strictString(args, "field");
|
|
251
|
+
const journalClass = strictString(args, "journal-class");
|
|
252
|
+
if (!articleType || !field || !journalClass) {
|
|
253
|
+
throw new CliError("research policy init requires --article-type, --field, and --journal-class.", { code: "RESEARCH_POLICY_INVALID", exitCode: 2 });
|
|
254
|
+
}
|
|
255
|
+
const root = await workspaceFromArgs(args);
|
|
256
|
+
const sourceRoot = strictString(args, "source-root") ??
|
|
257
|
+
(await resolveInstalledResearchPolicySource(root, io.env));
|
|
258
|
+
writeJson(io, await initializeResearchPolicy({
|
|
259
|
+
root,
|
|
260
|
+
projectId,
|
|
261
|
+
sourceRoot,
|
|
262
|
+
articleType,
|
|
263
|
+
field,
|
|
264
|
+
journalClass,
|
|
265
|
+
includeExactJournalTemplate: strictBoolean(args, "include-exact-journal-template"),
|
|
266
|
+
}), args);
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
if (["status", "validate", "approve", "resolve"].includes(action)) {
|
|
270
|
+
const args = parseStrictArgs(rest, {
|
|
271
|
+
...WORKSPACE_OPTIONS,
|
|
272
|
+
confirm: "boolean",
|
|
273
|
+
"acknowledge-defaults": "boolean",
|
|
274
|
+
}, `research policy ${action}`);
|
|
275
|
+
if (strictBoolean(args, "help"))
|
|
276
|
+
return writeHelp(io);
|
|
277
|
+
const projectId = onePositional(args.positionals, `research policy ${action}`);
|
|
278
|
+
const root = await workspaceFromArgs(args);
|
|
279
|
+
if (action === "approve") {
|
|
280
|
+
const result = await approveResearchPolicy(root, projectId, {
|
|
281
|
+
confirm: strictBoolean(args, "confirm"),
|
|
282
|
+
acknowledgeDefaults: strictBoolean(args, "acknowledge-defaults"),
|
|
283
|
+
});
|
|
284
|
+
writeJson(io, result, args);
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
if (action === "resolve") {
|
|
288
|
+
writeJson(io, await loadApprovedResearchPolicy(root, projectId), args);
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
const result = await inspectResearchPolicyStatus(root, projectId);
|
|
292
|
+
writeJson(io, result, args);
|
|
293
|
+
return action === "validate" &&
|
|
294
|
+
["missing", "invalid", "changed", "stale"].includes(result.status)
|
|
295
|
+
? 3
|
|
296
|
+
: 0;
|
|
297
|
+
}
|
|
298
|
+
throw unknownAction("research policy", action);
|
|
299
|
+
}
|
|
88
300
|
async function runSchema(argv, io) {
|
|
89
301
|
const [action, ...rest] = argv;
|
|
90
302
|
if (!action || action === "--help" || action === "-h")
|
|
@@ -95,6 +307,15 @@ async function runSchema(argv, io) {
|
|
|
95
307
|
if (strictBoolean(args, "help"))
|
|
96
308
|
return writeHelp(io);
|
|
97
309
|
const stage = onePositional(args.positionals, "research schema show");
|
|
310
|
+
if (stage === "publication-assessment") {
|
|
311
|
+
writeJson(io, publicationAssessmentSchema(), args);
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
if (stage.startsWith("publication-review-")) {
|
|
315
|
+
const role = publicationReviewRole(stage.slice("publication-review-".length));
|
|
316
|
+
writeJson(io, publicationReviewSchema(role), args);
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
98
319
|
if (stage !== "discover" &&
|
|
99
320
|
stage !== "acquire" &&
|
|
100
321
|
stage !== "analyze" &&
|
|
@@ -576,6 +797,7 @@ async function runProject(argv, io) {
|
|
|
576
797
|
const args = parseStrictArgs(rest, {
|
|
577
798
|
...WORKSPACE_OPTIONS,
|
|
578
799
|
question: "string",
|
|
800
|
+
goal: "string",
|
|
579
801
|
requirements: "string",
|
|
580
802
|
"input-plan": "string",
|
|
581
803
|
"confirm-budget": "boolean",
|
|
@@ -591,9 +813,10 @@ async function runProject(argv, io) {
|
|
|
591
813
|
});
|
|
592
814
|
}
|
|
593
815
|
const root = await workspaceFromArgs(args);
|
|
816
|
+
const goal = researchGoal(strictString(args, "goal"));
|
|
594
817
|
const requirementsPath = strictString(args, "requirements");
|
|
595
818
|
const inputPlanPath = strictString(args, "input-plan");
|
|
596
|
-
const project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : undefined);
|
|
819
|
+
const project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : undefined, goal === "top-journal" ? await loadApprovedResearchPolicy(root, projectId) : undefined);
|
|
597
820
|
writeJson(io, project, args);
|
|
598
821
|
return 0;
|
|
599
822
|
}
|
|
@@ -601,6 +824,8 @@ async function runProject(argv, io) {
|
|
|
601
824
|
const args = parseStrictArgs(rest, {
|
|
602
825
|
...WORKSPACE_OPTIONS,
|
|
603
826
|
question: "string",
|
|
827
|
+
goal: "string",
|
|
828
|
+
"policy-project": "string",
|
|
604
829
|
requirements: "string",
|
|
605
830
|
"input-plan": "string",
|
|
606
831
|
}, "research project preflight");
|
|
@@ -615,6 +840,23 @@ async function runProject(argv, io) {
|
|
|
615
840
|
});
|
|
616
841
|
}
|
|
617
842
|
const root = await workspaceFromArgs(args);
|
|
843
|
+
const goal = researchGoal(strictString(args, "goal"));
|
|
844
|
+
const policyProject = strictString(args, "policy-project");
|
|
845
|
+
if (goal === "top-journal") {
|
|
846
|
+
if (!policyProject) {
|
|
847
|
+
throw new CliError("Top-journal preflight requires --policy-project.", {
|
|
848
|
+
code: "RESEARCH_POLICY_REQUIRED",
|
|
849
|
+
exitCode: 2,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
await loadApprovedResearchPolicy(root, policyProject);
|
|
853
|
+
}
|
|
854
|
+
else if (policyProject) {
|
|
855
|
+
throw new CliError("--policy-project is only valid with --goal top-journal.", {
|
|
856
|
+
code: "RESEARCH_POLICY_INVALID",
|
|
857
|
+
exitCode: 2,
|
|
858
|
+
});
|
|
859
|
+
}
|
|
618
860
|
const requirementsPath = strictString(args, "requirements");
|
|
619
861
|
const requirements = requirementsPath ? await readEvidenceRequirements(requirementsPath) : null;
|
|
620
862
|
const inputPlanPath = strictString(args, "input-plan");
|
|
@@ -629,7 +871,13 @@ async function runProject(argv, io) {
|
|
|
629
871
|
return writeHelp(io);
|
|
630
872
|
if (inputAction !== "add")
|
|
631
873
|
throw unknownAction("research project input", inputAction ?? "");
|
|
632
|
-
const args = parseStrictArgs(inputRest, {
|
|
874
|
+
const args = parseStrictArgs(inputRest, {
|
|
875
|
+
...WORKSPACE_OPTIONS,
|
|
876
|
+
path: "string",
|
|
877
|
+
role: "string",
|
|
878
|
+
"trust-status": "string",
|
|
879
|
+
"independently-reproduced": "boolean",
|
|
880
|
+
}, "research project input add");
|
|
633
881
|
if (strictBoolean(args, "help"))
|
|
634
882
|
return writeHelp(io);
|
|
635
883
|
const projectId = onePositional(args.positionals, "research project input add");
|
|
@@ -642,7 +890,11 @@ async function runProject(argv, io) {
|
|
|
642
890
|
}
|
|
643
891
|
const role = inputRole(strictString(args, "role"));
|
|
644
892
|
const root = await workspaceFromArgs(args);
|
|
645
|
-
const
|
|
893
|
+
const trustStatus = projectInputTrustStatus(strictString(args, "trust-status"), role);
|
|
894
|
+
const input = await addProjectInput(root, projectId, inputPath, role, {
|
|
895
|
+
...(trustStatus ? { trustStatus } : {}),
|
|
896
|
+
independentlyReproduced: strictBoolean(args, "independently-reproduced"),
|
|
897
|
+
});
|
|
646
898
|
writeJson(io, input, args);
|
|
647
899
|
return 0;
|
|
648
900
|
}
|
|
@@ -744,6 +996,9 @@ async function runStatus(argv, io) {
|
|
|
744
996
|
const nativeStage = await inspectNativeResearchStage(root, current);
|
|
745
997
|
const snapshot = await inspectSnapshotForStatus(root, current.id);
|
|
746
998
|
const readyPackage = nextReadyPackage(current)?.id ?? null;
|
|
999
|
+
const publication = current.publicationPolicy
|
|
1000
|
+
? await inspectPublicationForStatus(root, current.id)
|
|
1001
|
+
: null;
|
|
747
1002
|
return {
|
|
748
1003
|
id: current.id,
|
|
749
1004
|
question: current.question,
|
|
@@ -754,8 +1009,9 @@ async function runStatus(argv, io) {
|
|
|
754
1009
|
evidenceState: current.evidenceState,
|
|
755
1010
|
snapshot,
|
|
756
1011
|
nativeStage,
|
|
1012
|
+
publication,
|
|
757
1013
|
readyPackage,
|
|
758
|
-
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage),
|
|
1014
|
+
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, publication),
|
|
759
1015
|
usage: current.usage,
|
|
760
1016
|
inputs: current.inputs,
|
|
761
1017
|
packages: current.packages,
|
|
@@ -812,7 +1068,18 @@ async function inspectSnapshotForStatus(root, projectId) {
|
|
|
812
1068
|
};
|
|
813
1069
|
}
|
|
814
1070
|
}
|
|
815
|
-
function
|
|
1071
|
+
async function inspectPublicationForStatus(root, projectId) {
|
|
1072
|
+
try {
|
|
1073
|
+
return await inspectPublicationStatus(root, projectId);
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
return {
|
|
1077
|
+
generationStatus: "invalid",
|
|
1078
|
+
code: error instanceof CliError ? error.code : "RESEARCH_PUBLICATION_STATE_INVALID",
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function projectRecommendedAction(root, project, readyPackage, nativeStage, publication) {
|
|
816
1083
|
if (project.lineage.supersededBy) {
|
|
817
1084
|
return `Continue with superseding project ${project.lineage.supersededBy}.`;
|
|
818
1085
|
}
|
|
@@ -832,6 +1099,21 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage) {
|
|
|
832
1099
|
return nativeStage.recommendedAction ?? "Resume the active native stage.";
|
|
833
1100
|
}
|
|
834
1101
|
if (project.status === "complete") {
|
|
1102
|
+
if (project.publicationPolicy) {
|
|
1103
|
+
if (publication && "code" in publication) {
|
|
1104
|
+
return `Publication state is invalid (${publication.code}); inspect Research Policy and frozen object bindings before continuing.`;
|
|
1105
|
+
}
|
|
1106
|
+
if (!publication || publication.generationStatus === "not-started") {
|
|
1107
|
+
return `Freeze the final manuscript and publication assessment, then inspect publication status: tiangong-ai research publication freeze ${project.id} --help`;
|
|
1108
|
+
}
|
|
1109
|
+
if (publication.closureSha256) {
|
|
1110
|
+
return `Publication closure is complete at ${publication.readinessVerdict}; inspect the immutable closure before post-closure authoring.`;
|
|
1111
|
+
}
|
|
1112
|
+
if (publication.reviewState !== "complete") {
|
|
1113
|
+
return `Complete fresh independent publication reviews for ${publication.missingReviewRoles.join(", ")}; inspect: tiangong-ai research publication status ${project.id} --workspace ${root}`;
|
|
1114
|
+
}
|
|
1115
|
+
return `Mechanically close the reviewed frozen manuscript: tiangong-ai research publication close ${project.id} --workspace ${root}`;
|
|
1116
|
+
}
|
|
835
1117
|
return `Create an immutable evidence addendum only when new evidence exists: tiangong-ai research project addendum ${project.id} --to <new-project-id> --workspace ${root}`;
|
|
836
1118
|
}
|
|
837
1119
|
if (project.status === "blocked") {
|
|
@@ -933,6 +1215,16 @@ function researchMode(value) {
|
|
|
933
1215
|
exitCode: 2,
|
|
934
1216
|
});
|
|
935
1217
|
}
|
|
1218
|
+
function researchGoal(value) {
|
|
1219
|
+
if (!value || value === "evidence-report")
|
|
1220
|
+
return "evidence-report";
|
|
1221
|
+
if (value === "top-journal")
|
|
1222
|
+
return value;
|
|
1223
|
+
throw new CliError(`Unsupported research goal: ${value}`, {
|
|
1224
|
+
code: "RESEARCH_GOAL_INVALID",
|
|
1225
|
+
exitCode: 2,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
936
1228
|
function resumeStage(value) {
|
|
937
1229
|
if (!value)
|
|
938
1230
|
return undefined;
|
|
@@ -1034,6 +1326,71 @@ function inputRole(value) {
|
|
|
1034
1326
|
exitCode: 2,
|
|
1035
1327
|
});
|
|
1036
1328
|
}
|
|
1329
|
+
function projectInputTrustStatus(value, role) {
|
|
1330
|
+
if (!value)
|
|
1331
|
+
return undefined;
|
|
1332
|
+
if (value === "verified-owner-input" ||
|
|
1333
|
+
value === "unverified-owner-input" ||
|
|
1334
|
+
value === "reference-only" ||
|
|
1335
|
+
value === "replication-candidate") {
|
|
1336
|
+
return value;
|
|
1337
|
+
}
|
|
1338
|
+
throw new CliError(`Unsupported trust status for ${role} input: ${value}`, {
|
|
1339
|
+
code: "RESEARCH_INPUT_TRUST_INVALID",
|
|
1340
|
+
exitCode: 2,
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
function publicationReviewRole(value) {
|
|
1344
|
+
if (value === "evidence" ||
|
|
1345
|
+
value === "methods-reproducibility" ||
|
|
1346
|
+
value === "domain-novelty" ||
|
|
1347
|
+
value === "journal-editor") {
|
|
1348
|
+
return value;
|
|
1349
|
+
}
|
|
1350
|
+
throw new CliError("--role must be evidence, methods-reproducibility, domain-novelty, or journal-editor.", { code: "RESEARCH_PUBLICATION_REVIEW_ROLE_INVALID", exitCode: 2 });
|
|
1351
|
+
}
|
|
1352
|
+
function publicationAgent(value, label) {
|
|
1353
|
+
if (value === "codex" || value === "claude")
|
|
1354
|
+
return value;
|
|
1355
|
+
throw new CliError(`--${label}-agent must be codex or claude.`, {
|
|
1356
|
+
code: "RESEARCH_PUBLICATION_AGENT_INVALID",
|
|
1357
|
+
exitCode: 2,
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
async function readAbsolutePathArray(path) {
|
|
1361
|
+
if (!isAbsolute(path)) {
|
|
1362
|
+
throw new CliError("--supplements must be an absolute JSON file path.", {
|
|
1363
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1364
|
+
exitCode: 2,
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
const selected = resolve(path);
|
|
1368
|
+
const info = await lstat(selected).catch(() => undefined);
|
|
1369
|
+
if (!info?.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) {
|
|
1370
|
+
throw new CliError("--supplements must be a bounded regular non-symlink JSON file.", {
|
|
1371
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1372
|
+
exitCode: 2,
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
let value;
|
|
1376
|
+
try {
|
|
1377
|
+
value = JSON.parse(await readFile(selected, "utf8"));
|
|
1378
|
+
}
|
|
1379
|
+
catch {
|
|
1380
|
+
throw new CliError("--supplements contains invalid JSON.", {
|
|
1381
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1382
|
+
exitCode: 2,
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
if (!Array.isArray(value) ||
|
|
1386
|
+
value.some((item) => typeof item !== "string" || !isAbsolute(item) || resolve(item) !== item)) {
|
|
1387
|
+
throw new CliError("--supplements must contain an array of absolute canonical paths.", {
|
|
1388
|
+
code: "RESEARCH_PUBLICATION_FILE_INVALID",
|
|
1389
|
+
exitCode: 2,
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
return value;
|
|
1393
|
+
}
|
|
1037
1394
|
function integerOption(value, fallback, label) {
|
|
1038
1395
|
if (!value)
|
|
1039
1396
|
return fallback;
|