@akagilnc/pi-workflow-roles 0.1.2193 → 0.1.2203
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/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/activation-ledger-git.js +12 -2
- package/dist/public-cli/main.js +194 -50
- package/package.json +1 -1
- package/src/activation-ledger-git.ts +33 -5
- package/src/public-cli/cli.ts +6 -7
- package/src/public-cli/invocation.ts +115 -11
- package/src/public-cli/option-definitions.ts +10 -9
- package/src/public-cli/taishi-run.ts +25 -1
- package/src/taishi-book-key.ts +66 -0
- package/src/taishi-cohort.ts +19 -7
- package/src/taishi-entry.ts +26 -21
- package/src/taishi-index.ts +53 -8
- package/src/taishi-ledger.ts +13 -3
package/README.md
CHANGED
|
@@ -170,7 +170,7 @@ Generated from `src/public-cli/option-definitions.ts`. Prefer `ak-role help <com
|
|
|
170
170
|
| `--attach` | — | `path` | when:sweep | yes | option | modes=sweep; max=sweep:1 | Sweep-mode attachment path; required exactly once in sweep; payload is the attachment body. |
|
|
171
171
|
| `--cohort` | — | — | no | no | option | modes=cohort | Select cohort mode. |
|
|
172
172
|
| `--group-a-label` | — | `label` | when:cohort | no | option | modes=cohort | Cohort group A label (required in cohort mode). |
|
|
173
|
-
| `--group-a-issues` | — | `N[
|
|
173
|
+
| `--group-a-issues` | — | `N\|book:N[,...]` | when:cohort | no | option | modes=cohort | Cohort group A issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \, / \\ (required in cohort mode). |
|
|
174
174
|
| `--group-b-label` | — | `label` | when:cohort | no | option | modes=cohort | Cohort group B label (required in cohort mode). |
|
|
175
|
-
| `--group-b-issues` | — | `N[
|
|
175
|
+
| `--group-b-issues` | — | `N\|book:N[,...]` | when:cohort | no | option | modes=cohort | Cohort group B issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \, / \\ (required in cohort mode). |
|
|
176
176
|
<!-- END GENERATED: public-cli-options -->
|
package/README.zh-CN.md
CHANGED
|
@@ -199,7 +199,7 @@ ak-role merger --project /path/to/worktree "Reconcile the active merge."
|
|
|
199
199
|
| `--attach` | — | `path` | 条件:sweep | 是 | option | modes=sweep; max=sweep:1 | sweep 模式附件路径;sweep 必填且恰一次;载荷为附件正文。 |
|
|
200
200
|
| `--cohort` | — | — | 否 | 否 | option | modes=cohort | 选择 cohort 模式。 |
|
|
201
201
|
| `--group-a-label` | — | `label` | 条件:cohort | 否 | option | modes=cohort | cohort A 组标签(cohort 模式必填)。 |
|
|
202
|
-
| `--group-a-issues` | — | `N[
|
|
202
|
+
| `--group-a-issues` | — | `N\|book:N[,...]` | 条件:cohort | 否 | option | modes=cohort | cohort A 组 issue:裸 N 归属 cwd 簿;book:N 显式跨簿;簿键中的逗号/反斜杠用 \, / \\ 转义(cohort 模式必填)。 |
|
|
203
203
|
| `--group-b-label` | — | `label` | 条件:cohort | 否 | option | modes=cohort | cohort B 组标签(cohort 模式必填)。 |
|
|
204
|
-
| `--group-b-issues` | — | `N[
|
|
204
|
+
| `--group-b-issues` | — | `N\|book:N[,...]` | 条件:cohort | 否 | option | modes=cohort | cohort B 组 issue:裸 N 归属 cwd 簿;book:N 显式跨簿;簿键中的逗号/反斜杠用 \, / \\ 转义(cohort 模式必填)。 |
|
|
205
205
|
<!-- END GENERATED: public-cli-options -->
|
|
@@ -8,20 +8,26 @@ const GIT_DISCOVERY_ENV_KEYS = [
|
|
|
8
8
|
"GIT_DISCOVERY_ACROSS_FILESYSTEM"
|
|
9
9
|
];
|
|
10
10
|
function envWithoutGitDiscovery(base = process.env) {
|
|
11
|
-
const env = { ...base };
|
|
11
|
+
const env = { ...base, LC_ALL: "C" };
|
|
12
12
|
for (const key of GIT_DISCOVERY_ENV_KEYS) {
|
|
13
13
|
delete env[key];
|
|
14
14
|
}
|
|
15
15
|
return env;
|
|
16
16
|
}
|
|
17
|
+
const CONFIRMED_NON_REPOSITORY_STDERR = /^fatal:\s*not a git repository/i;
|
|
18
|
+
function isConfirmedNonRepositoryStderr(stderr) {
|
|
19
|
+
return CONFIRMED_NON_REPOSITORY_STDERR.test(stderr);
|
|
20
|
+
}
|
|
17
21
|
class ActivationGitRepositoryRequiredError extends Error {
|
|
18
22
|
code = "AK_ACTIVATION_GIT_REPOSITORY_REQUIRED";
|
|
23
|
+
confirmedNonRepository;
|
|
19
24
|
constructor(detail, options) {
|
|
20
25
|
super(
|
|
21
26
|
`Workflow role activation requires a git repository cwd (git rev-parse --git-common-dir failed): ${detail || "unknown git error"}`,
|
|
22
27
|
options?.cause === void 0 ? void 0 : { cause: options.cause }
|
|
23
28
|
);
|
|
24
29
|
this.name = "ActivationGitRepositoryRequiredError";
|
|
30
|
+
this.confirmedNonRepository = options?.confirmedNonRepository ?? false;
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
function isGitSpawnInfrastructureError(error) {
|
|
@@ -49,7 +55,10 @@ function resolveBookKeyFromGit(cwd) {
|
|
|
49
55
|
}
|
|
50
56
|
const err = error;
|
|
51
57
|
const detail = typeof err.stderr === "string" ? err.stderr.trim() : Buffer.isBuffer(err.stderr) ? err.stderr.toString("utf8").trim() : typeof err.message === "string" ? err.message : "";
|
|
52
|
-
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
58
|
+
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
59
|
+
cause: error,
|
|
60
|
+
confirmedNonRepository: isConfirmedNonRepositoryStderr(detail)
|
|
61
|
+
});
|
|
53
62
|
}
|
|
54
63
|
if (commonDir.length === 0) {
|
|
55
64
|
throw new Error("git rev-parse --git-common-dir returned an empty path");
|
|
@@ -64,5 +73,6 @@ function resolveBookKeyFromGit(cwd) {
|
|
|
64
73
|
}
|
|
65
74
|
export {
|
|
66
75
|
ActivationGitRepositoryRequiredError,
|
|
76
|
+
isConfirmedNonRepositoryStderr,
|
|
67
77
|
resolveBookKeyFromGit
|
|
68
78
|
};
|
package/dist/public-cli/main.js
CHANGED
|
@@ -15080,12 +15080,15 @@ var init_activation_ledger_topology = __esm({
|
|
|
15080
15080
|
import { execFileSync } from "node:child_process";
|
|
15081
15081
|
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
15082
15082
|
function envWithoutGitDiscovery(base = process.env) {
|
|
15083
|
-
const env = { ...base };
|
|
15083
|
+
const env = { ...base, LC_ALL: "C" };
|
|
15084
15084
|
for (const key of GIT_DISCOVERY_ENV_KEYS) {
|
|
15085
15085
|
delete env[key];
|
|
15086
15086
|
}
|
|
15087
15087
|
return env;
|
|
15088
15088
|
}
|
|
15089
|
+
function isConfirmedNonRepositoryStderr(stderr) {
|
|
15090
|
+
return CONFIRMED_NON_REPOSITORY_STDERR.test(stderr);
|
|
15091
|
+
}
|
|
15089
15092
|
function isGitSpawnInfrastructureError(error) {
|
|
15090
15093
|
if (error === null || typeof error !== "object" || !("code" in error)) return false;
|
|
15091
15094
|
const code = error.code;
|
|
@@ -15111,7 +15114,10 @@ function resolveBookKeyFromGit(cwd) {
|
|
|
15111
15114
|
}
|
|
15112
15115
|
const err = error;
|
|
15113
15116
|
const detail = typeof err.stderr === "string" ? err.stderr.trim() : Buffer.isBuffer(err.stderr) ? err.stderr.toString("utf8").trim() : typeof err.message === "string" ? err.message : "";
|
|
15114
|
-
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
15117
|
+
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
15118
|
+
cause: error,
|
|
15119
|
+
confirmedNonRepository: isConfirmedNonRepositoryStderr(detail)
|
|
15120
|
+
});
|
|
15115
15121
|
}
|
|
15116
15122
|
if (commonDir.length === 0) {
|
|
15117
15123
|
throw new Error("git rev-parse --git-common-dir returned an empty path");
|
|
@@ -15124,7 +15130,7 @@ function resolveBookKeyFromGit(cwd) {
|
|
|
15124
15130
|
}
|
|
15125
15131
|
return bookKey;
|
|
15126
15132
|
}
|
|
15127
|
-
var GIT_DISCOVERY_ENV_KEYS, ActivationGitRepositoryRequiredError;
|
|
15133
|
+
var GIT_DISCOVERY_ENV_KEYS, CONFIRMED_NON_REPOSITORY_STDERR, ActivationGitRepositoryRequiredError;
|
|
15128
15134
|
var init_activation_ledger_git = __esm({
|
|
15129
15135
|
"src/activation-ledger-git.ts"() {
|
|
15130
15136
|
"use strict";
|
|
@@ -15135,14 +15141,17 @@ var init_activation_ledger_git = __esm({
|
|
|
15135
15141
|
"GIT_CEILING_DIRECTORIES",
|
|
15136
15142
|
"GIT_DISCOVERY_ACROSS_FILESYSTEM"
|
|
15137
15143
|
];
|
|
15144
|
+
CONFIRMED_NON_REPOSITORY_STDERR = /^fatal:\s*not a git repository/i;
|
|
15138
15145
|
ActivationGitRepositoryRequiredError = class extends Error {
|
|
15139
15146
|
code = "AK_ACTIVATION_GIT_REPOSITORY_REQUIRED";
|
|
15147
|
+
confirmedNonRepository;
|
|
15140
15148
|
constructor(detail, options) {
|
|
15141
15149
|
super(
|
|
15142
15150
|
`Workflow role activation requires a git repository cwd (git rev-parse --git-common-dir failed): ${detail || "unknown git error"}`,
|
|
15143
15151
|
options?.cause === void 0 ? void 0 : { cause: options.cause }
|
|
15144
15152
|
);
|
|
15145
15153
|
this.name = "ActivationGitRepositoryRequiredError";
|
|
15154
|
+
this.confirmedNonRepository = options?.confirmedNonRepository ?? false;
|
|
15146
15155
|
}
|
|
15147
15156
|
};
|
|
15148
15157
|
}
|
|
@@ -15752,7 +15761,7 @@ function evaluateTaishiModeOptionContract(mode, counts) {
|
|
|
15752
15761
|
if (mode === "cohort") {
|
|
15753
15762
|
return {
|
|
15754
15763
|
ok: false,
|
|
15755
|
-
message: "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[
|
|
15764
|
+
message: "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N|book:N[,...]> --group-b-label <L> --group-b-issues <N|book:N[,...]>"
|
|
15756
15765
|
};
|
|
15757
15766
|
}
|
|
15758
15767
|
return {
|
|
@@ -15922,7 +15931,7 @@ function renderHumanOwnerOptionLines(owner, locale2 = "en") {
|
|
|
15922
15931
|
if (opt.aliases.length > 0) {
|
|
15923
15932
|
const aliasHint = opt.aliases.join(", ");
|
|
15924
15933
|
if (opt.form === "positional") {
|
|
15925
|
-
spelling = opt.aliases.
|
|
15934
|
+
spelling = opt.aliases.join("|");
|
|
15926
15935
|
} else {
|
|
15927
15936
|
spelling = `${spelling} (${aliasHint})`;
|
|
15928
15937
|
}
|
|
@@ -16339,15 +16348,15 @@ var init_option_definitions = __esm({
|
|
|
16339
16348
|
owner: "taishi",
|
|
16340
16349
|
canonical: "--group-a-issues",
|
|
16341
16350
|
aliases: [],
|
|
16342
|
-
valueMetavar: "N[
|
|
16351
|
+
valueMetavar: "N|book:N[,...]",
|
|
16343
16352
|
required: false,
|
|
16344
16353
|
repeatable: false,
|
|
16345
16354
|
form: "option",
|
|
16346
16355
|
modes: ["cohort"],
|
|
16347
16356
|
requiredInModes: ["cohort"],
|
|
16348
16357
|
description: {
|
|
16349
|
-
en: "Cohort group A comma
|
|
16350
|
-
zh: "cohort A \u7EC4\u9017\u53F7\
|
|
16358
|
+
en: "Cohort group A issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \\, / \\\\ (required in cohort mode).",
|
|
16359
|
+
zh: "cohort A \u7EC4 issue\uFF1A\u88F8 N \u5F52\u5C5E cwd \u7C3F\uFF1Bbook:N \u663E\u5F0F\u8DE8\u7C3F\uFF1B\u7C3F\u952E\u4E2D\u7684\u9017\u53F7/\u53CD\u659C\u6760\u7528 \\, / \\\\ \u8F6C\u4E49\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
|
|
16351
16360
|
}
|
|
16352
16361
|
},
|
|
16353
16362
|
{
|
|
@@ -16371,15 +16380,15 @@ var init_option_definitions = __esm({
|
|
|
16371
16380
|
owner: "taishi",
|
|
16372
16381
|
canonical: "--group-b-issues",
|
|
16373
16382
|
aliases: [],
|
|
16374
|
-
valueMetavar: "N[
|
|
16383
|
+
valueMetavar: "N|book:N[,...]",
|
|
16375
16384
|
required: false,
|
|
16376
16385
|
repeatable: false,
|
|
16377
16386
|
form: "option",
|
|
16378
16387
|
modes: ["cohort"],
|
|
16379
16388
|
requiredInModes: ["cohort"],
|
|
16380
16389
|
description: {
|
|
16381
|
-
en: "Cohort group B comma
|
|
16382
|
-
zh: "cohort B \u7EC4\u9017\u53F7\
|
|
16390
|
+
en: "Cohort group B issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \\, / \\\\ (required in cohort mode).",
|
|
16391
|
+
zh: "cohort B \u7EC4 issue\uFF1A\u88F8 N \u5F52\u5C5E cwd \u7C3F\uFF1Bbook:N \u663E\u5F0F\u8DE8\u7C3F\uFF1B\u7C3F\u952E\u4E2D\u7684\u9017\u53F7/\u53CD\u659C\u6760\u7528 \\, / \\\\ \u8F6C\u4E49\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
|
|
16383
16392
|
}
|
|
16384
16393
|
}
|
|
16385
16394
|
];
|
|
@@ -16474,7 +16483,7 @@ var init_option_definitions = __esm({
|
|
|
16474
16483
|
usage: [
|
|
16475
16484
|
"ak-role taishi [--ticket <N>]",
|
|
16476
16485
|
"ak-role taishi [sweep] --attach <path>",
|
|
16477
|
-
"ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[
|
|
16486
|
+
"ak-role taishi --cohort --group-a-label <L> --group-a-issues <N|book:N[,...]> --group-b-label <L> --group-b-issues <N|book:N[,...]>"
|
|
16478
16487
|
],
|
|
16479
16488
|
examples: [
|
|
16480
16489
|
"ak-role taishi",
|
|
@@ -17906,16 +17915,75 @@ function parseTaishiTicketNumber(raw, flag = "--ticket") {
|
|
|
17906
17915
|
}
|
|
17907
17916
|
return value;
|
|
17908
17917
|
}
|
|
17909
|
-
function
|
|
17918
|
+
function parseTaishiCohortIssueToken(raw, flag) {
|
|
17919
|
+
const trimmed = raw.trim();
|
|
17920
|
+
if (trimmed === "") {
|
|
17921
|
+
throw new CliUsageError(
|
|
17922
|
+
`${flag} requires a comma-separated list of N or book:N`
|
|
17923
|
+
);
|
|
17924
|
+
}
|
|
17925
|
+
const sep4 = trimmed.lastIndexOf(":");
|
|
17926
|
+
if (sep4 > 0) {
|
|
17927
|
+
const rhs = trimmed.slice(sep4 + 1);
|
|
17928
|
+
if (TAISHI_TICKET_NUMBER_PATTERN.test(rhs)) {
|
|
17929
|
+
const bookKey = trimmed.slice(0, sep4);
|
|
17930
|
+
if (bookKey.trim() === "") {
|
|
17931
|
+
throw new CliUsageError(
|
|
17932
|
+
`${flag} book:N requires a non-empty book key, got ${raw}`
|
|
17933
|
+
);
|
|
17934
|
+
}
|
|
17935
|
+
return {
|
|
17936
|
+
kind: "book-qualified",
|
|
17937
|
+
bookKey,
|
|
17938
|
+
issueNumber: parseTaishiTicketNumber(rhs, flag)
|
|
17939
|
+
};
|
|
17940
|
+
}
|
|
17941
|
+
}
|
|
17942
|
+
return {
|
|
17943
|
+
kind: "bare",
|
|
17944
|
+
issueNumber: parseTaishiTicketNumber(trimmed, flag)
|
|
17945
|
+
};
|
|
17946
|
+
}
|
|
17947
|
+
function splitTaishiCohortIssueListParts(raw) {
|
|
17948
|
+
const parts = [];
|
|
17949
|
+
let current = "";
|
|
17950
|
+
let escaped = false;
|
|
17951
|
+
for (const ch of raw) {
|
|
17952
|
+
if (escaped) {
|
|
17953
|
+
current += ch === "," || ch === "\\" ? ch : `\\${ch}`;
|
|
17954
|
+
escaped = false;
|
|
17955
|
+
continue;
|
|
17956
|
+
}
|
|
17957
|
+
if (ch === "\\") {
|
|
17958
|
+
escaped = true;
|
|
17959
|
+
continue;
|
|
17960
|
+
}
|
|
17961
|
+
if (ch === ",") {
|
|
17962
|
+
parts.push(current);
|
|
17963
|
+
current = "";
|
|
17964
|
+
continue;
|
|
17965
|
+
}
|
|
17966
|
+
current += ch;
|
|
17967
|
+
}
|
|
17968
|
+
parts.push(escaped ? `${current}\\` : current);
|
|
17969
|
+
return parts;
|
|
17970
|
+
}
|
|
17971
|
+
function parseTaishiCohortIssueTokenList(raw, flag) {
|
|
17910
17972
|
const trimmed = raw.trim();
|
|
17911
17973
|
if (trimmed === "") {
|
|
17912
|
-
throw new CliUsageError(
|
|
17974
|
+
throw new CliUsageError(
|
|
17975
|
+
`${flag} requires a comma-separated list of N or book:N`
|
|
17976
|
+
);
|
|
17913
17977
|
}
|
|
17914
|
-
const parts = trimmed
|
|
17978
|
+
const parts = splitTaishiCohortIssueListParts(trimmed).map(
|
|
17979
|
+
(part) => part.trim()
|
|
17980
|
+
);
|
|
17915
17981
|
if (parts.some((part) => part === "")) {
|
|
17916
|
-
throw new CliUsageError(
|
|
17982
|
+
throw new CliUsageError(
|
|
17983
|
+
`${flag} requires a comma-separated list of N or book:N`
|
|
17984
|
+
);
|
|
17917
17985
|
}
|
|
17918
|
-
return parts.map((part) =>
|
|
17986
|
+
return parts.map((part) => parseTaishiCohortIssueToken(part, flag));
|
|
17919
17987
|
}
|
|
17920
17988
|
function requireOptionValue(flag, value, what) {
|
|
17921
17989
|
if (value === void 0 || value.trim() === "") {
|
|
@@ -17974,7 +18042,7 @@ function parseTaishiArgv(args) {
|
|
|
17974
18042
|
requireOptionValue(
|
|
17975
18043
|
taken.def.canonical,
|
|
17976
18044
|
taken.value,
|
|
17977
|
-
"a comma-separated
|
|
18045
|
+
"a comma-separated list of N or book:N"
|
|
17978
18046
|
)
|
|
17979
18047
|
);
|
|
17980
18048
|
continue;
|
|
@@ -18025,11 +18093,17 @@ function parseTaishiArgv(args) {
|
|
|
18025
18093
|
groups: [
|
|
18026
18094
|
{
|
|
18027
18095
|
groupLabel: groupALabel,
|
|
18028
|
-
issues:
|
|
18096
|
+
issues: parseTaishiCohortIssueTokenList(
|
|
18097
|
+
groupAIssuesRaw,
|
|
18098
|
+
"--group-a-issues"
|
|
18099
|
+
)
|
|
18029
18100
|
},
|
|
18030
18101
|
{
|
|
18031
18102
|
groupLabel: groupBLabel,
|
|
18032
|
-
issues:
|
|
18103
|
+
issues: parseTaishiCohortIssueTokenList(
|
|
18104
|
+
groupBIssuesRaw,
|
|
18105
|
+
"--group-b-issues"
|
|
18106
|
+
)
|
|
18033
18107
|
}
|
|
18034
18108
|
]
|
|
18035
18109
|
};
|
|
@@ -25457,6 +25531,40 @@ var init_reviewer_run = __esm({
|
|
|
25457
25531
|
}
|
|
25458
25532
|
});
|
|
25459
25533
|
|
|
25534
|
+
// src/taishi-book-key.ts
|
|
25535
|
+
import { statSync as statSync2 } from "node:fs";
|
|
25536
|
+
import { isAbsolute as isAbsolute5 } from "node:path";
|
|
25537
|
+
function resolveTaishiBookKey(projectRoot) {
|
|
25538
|
+
const identity = physicalPathIdentity(projectRoot);
|
|
25539
|
+
let stats;
|
|
25540
|
+
try {
|
|
25541
|
+
stats = statSync2(identity);
|
|
25542
|
+
} catch (error) {
|
|
25543
|
+
const code = errnoCode(error);
|
|
25544
|
+
if (code === "ENOENT" || code === "ENOTDIR") return `root:${identity}`;
|
|
25545
|
+
throw error;
|
|
25546
|
+
}
|
|
25547
|
+
if (!stats.isDirectory()) return `root:${identity}`;
|
|
25548
|
+
try {
|
|
25549
|
+
return resolveBookKeyFromGit(identity);
|
|
25550
|
+
} catch (error) {
|
|
25551
|
+
if (error instanceof ActivationGitRepositoryRequiredError && error.confirmedNonRepository) {
|
|
25552
|
+
return `root:${identity}`;
|
|
25553
|
+
}
|
|
25554
|
+
throw error;
|
|
25555
|
+
}
|
|
25556
|
+
}
|
|
25557
|
+
function isSyntheticTaishiBookKey(bookKey) {
|
|
25558
|
+
return bookKey.startsWith("root:") && isAbsolute5(bookKey.slice("root:".length));
|
|
25559
|
+
}
|
|
25560
|
+
var init_taishi_book_key = __esm({
|
|
25561
|
+
"src/taishi-book-key.ts"() {
|
|
25562
|
+
"use strict";
|
|
25563
|
+
init_activation_ledger_git();
|
|
25564
|
+
init_activation_ledger_topology();
|
|
25565
|
+
}
|
|
25566
|
+
});
|
|
25567
|
+
|
|
25460
25568
|
// src/atomic-write.ts
|
|
25461
25569
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25462
25570
|
import { rename, rm, writeFile as writeFile13 } from "node:fs/promises";
|
|
@@ -25530,6 +25638,15 @@ function rowFromIssueMetricsPage(page) {
|
|
|
25530
25638
|
lastActivityAt: page.lastActivityAt
|
|
25531
25639
|
};
|
|
25532
25640
|
}
|
|
25641
|
+
function normalizeTaishiLibraryIndexRow(row) {
|
|
25642
|
+
const rawBook = row.bookKey;
|
|
25643
|
+
if (typeof rawBook === "string" && rawBook !== "") {
|
|
25644
|
+
if (rawBook === row.bookKey) return row;
|
|
25645
|
+
return { ...row, bookKey: rawBook };
|
|
25646
|
+
}
|
|
25647
|
+
const projectRoot = physicalPathIdentity(row.projectRoot);
|
|
25648
|
+
return { ...row, projectRoot, bookKey: resolveTaishiBookKey(projectRoot) };
|
|
25649
|
+
}
|
|
25533
25650
|
function sortRows(rows) {
|
|
25534
25651
|
return [...rows].sort((a, b) => {
|
|
25535
25652
|
const byBook = a.bookKey.localeCompare(b.bookKey);
|
|
@@ -25547,12 +25664,14 @@ function sortRows(rows) {
|
|
|
25547
25664
|
function buildTaishiLibraryIndexPage(rows) {
|
|
25548
25665
|
return {
|
|
25549
25666
|
kind: "taishi-library-index",
|
|
25550
|
-
rows: sortRows(rows)
|
|
25667
|
+
rows: sortRows(rows.map(normalizeTaishiLibraryIndexRow))
|
|
25551
25668
|
};
|
|
25552
25669
|
}
|
|
25553
|
-
function findTaishiLibraryIndexRow(index, issueNumber) {
|
|
25670
|
+
function findTaishiLibraryIndexRow(index, issueNumber, bookKey) {
|
|
25554
25671
|
if (index === void 0) return void 0;
|
|
25555
|
-
return index.rows.find(
|
|
25672
|
+
return index.rows.find(
|
|
25673
|
+
(row) => row.issueNumber === issueNumber && row.bookKey === bookKey
|
|
25674
|
+
);
|
|
25556
25675
|
}
|
|
25557
25676
|
function indexRowKey(row) {
|
|
25558
25677
|
return `${row.bookKey}\0${row.projectRoot}`;
|
|
@@ -25564,6 +25683,7 @@ function upsertTaishiLibraryIndexRows(existing, upserts) {
|
|
|
25564
25683
|
const byKey = /* @__PURE__ */ new Map();
|
|
25565
25684
|
const keyByIssue = /* @__PURE__ */ new Map();
|
|
25566
25685
|
const ingest = (row) => {
|
|
25686
|
+
row = normalizeTaishiLibraryIndexRow(row);
|
|
25567
25687
|
const key = indexRowKey(row);
|
|
25568
25688
|
if (row.issueNumber !== void 0) {
|
|
25569
25689
|
const issueKey = issueBookKey(row.bookKey, row.issueNumber);
|
|
@@ -25602,7 +25722,14 @@ async function readTaishiLibraryIndexPage(ledgerHome) {
|
|
|
25602
25722
|
}
|
|
25603
25723
|
throw error;
|
|
25604
25724
|
}
|
|
25605
|
-
|
|
25725
|
+
const parsed = JSON.parse(raw);
|
|
25726
|
+
if (parsed === null || typeof parsed !== "object" || !Array.isArray(parsed.rows)) {
|
|
25727
|
+
const shape = parsed === null ? "null" : typeof parsed !== "object" ? typeof parsed : `object with non-array rows (${typeof parsed.rows})`;
|
|
25728
|
+
throw new Error(
|
|
25729
|
+
`taishi library-index at ${path} is malformed (${shape}; expected an index page with a rows array) \u2014 rejected at the read boundary`
|
|
25730
|
+
);
|
|
25731
|
+
}
|
|
25732
|
+
return buildTaishiLibraryIndexPage(parsed.rows);
|
|
25606
25733
|
}
|
|
25607
25734
|
async function writeTaishiLibraryIndexPage(ledgerHome, page) {
|
|
25608
25735
|
const path = taishiLibraryIndexPath(ledgerHome);
|
|
@@ -25626,6 +25753,7 @@ var init_taishi_index = __esm({
|
|
|
25626
25753
|
"use strict";
|
|
25627
25754
|
init_atomic_write();
|
|
25628
25755
|
init_activation_ledger_topology();
|
|
25756
|
+
init_taishi_book_key();
|
|
25629
25757
|
LIBRARY_INDEX_LOCK_NAME = ".library-index.lock";
|
|
25630
25758
|
LIBRARY_INDEX_LOCK_TIMEOUT_MS = 3e4;
|
|
25631
25759
|
LIBRARY_INDEX_LOCK_RETRY_MS = 15;
|
|
@@ -25692,10 +25820,11 @@ async function aggregateGroup(index, input, ensureIssuePage) {
|
|
|
25692
25820
|
let totalWallMs = 0;
|
|
25693
25821
|
let hasReworkSample = false;
|
|
25694
25822
|
const legWalls = [];
|
|
25695
|
-
for (const
|
|
25696
|
-
const
|
|
25823
|
+
for (const ref of input.issues) {
|
|
25824
|
+
const { issueNumber, bookKey } = ref;
|
|
25825
|
+
const row = findTaishiLibraryIndexRow(index, issueNumber, bookKey);
|
|
25697
25826
|
if (row === void 0) {
|
|
25698
|
-
issueEntries.push({ issueNumber, status: "absent" });
|
|
25827
|
+
issueEntries.push({ issueNumber, status: "absent", bookKey });
|
|
25699
25828
|
continue;
|
|
25700
25829
|
}
|
|
25701
25830
|
const page = await ensureIssuePage({
|
|
@@ -26270,7 +26399,12 @@ async function scanTaishiIssueRuns(input) {
|
|
|
26270
26399
|
let bookNames;
|
|
26271
26400
|
if (input.bookKey !== void 0 && input.bookKey.trim() !== "") {
|
|
26272
26401
|
bookNames = [input.bookKey];
|
|
26273
|
-
|
|
26402
|
+
if (input.projectRoot !== void 0) {
|
|
26403
|
+
wholeBook = false;
|
|
26404
|
+
scopeRootIdentity = physicalPathIdentity(input.projectRoot);
|
|
26405
|
+
} else {
|
|
26406
|
+
wholeBook = true;
|
|
26407
|
+
}
|
|
26274
26408
|
} else if (input.projectRoot !== void 0) {
|
|
26275
26409
|
const resolved = tryResolveBookKeyFromProjectRoot(input.projectRoot);
|
|
26276
26410
|
if (resolved !== void 0) {
|
|
@@ -27237,22 +27371,13 @@ function cachedPageMatchesRequestedScope(page, input) {
|
|
|
27237
27371
|
}
|
|
27238
27372
|
return page.issueNumber === requestedTicket;
|
|
27239
27373
|
}
|
|
27240
|
-
function tryResolveBookKey(projectRoot) {
|
|
27241
|
-
try {
|
|
27242
|
-
return resolveBookKeyFromGit(projectRoot);
|
|
27243
|
-
} catch {
|
|
27244
|
-
return void 0;
|
|
27245
|
-
}
|
|
27246
|
-
}
|
|
27247
27374
|
function resolveIssueBookKey(input) {
|
|
27248
27375
|
if (input.bookKey !== void 0 && input.bookKey.trim() !== "") {
|
|
27249
27376
|
return input.bookKey;
|
|
27250
27377
|
}
|
|
27251
|
-
|
|
27252
|
-
if (fromGit !== void 0) return fromGit;
|
|
27253
|
-
return `root:${physicalPathIdentity(input.projectRoot)}`;
|
|
27378
|
+
return resolveTaishiBookKey(input.projectRoot);
|
|
27254
27379
|
}
|
|
27255
|
-
async function readOrComputeTaishiIssuePage(input) {
|
|
27380
|
+
async function readOrComputeTaishiIssuePage(input, options) {
|
|
27256
27381
|
const ledgerHome = resolveActivationLedgerHome();
|
|
27257
27382
|
const projectRoot = physicalPathIdentity(input.projectRoot);
|
|
27258
27383
|
const bookKey = resolveIssueBookKey(input);
|
|
@@ -27280,7 +27405,7 @@ async function readOrComputeTaishiIssuePage(input) {
|
|
|
27280
27405
|
}
|
|
27281
27406
|
}
|
|
27282
27407
|
try {
|
|
27283
|
-
return await runTaishiIssueMode(input);
|
|
27408
|
+
return await runTaishiIssueMode(input, void 0, options?.scanProjectRoot);
|
|
27284
27409
|
} catch (error) {
|
|
27285
27410
|
if (error instanceof TaishiIssueComputeError) throw error;
|
|
27286
27411
|
throw new TaishiIssueComputeError({
|
|
@@ -27291,7 +27416,7 @@ async function readOrComputeTaishiIssuePage(input) {
|
|
|
27291
27416
|
});
|
|
27292
27417
|
}
|
|
27293
27418
|
}
|
|
27294
|
-
async function runTaishiIssueMode(input, precomputedScan) {
|
|
27419
|
+
async function runTaishiIssueMode(input, precomputedScan, scanProjectRoot) {
|
|
27295
27420
|
assertTaishiChangedLinesInput(input.changedLines);
|
|
27296
27421
|
const ledgerHome = resolveActivationLedgerHome();
|
|
27297
27422
|
const projectRoot = input.projectRoot;
|
|
@@ -27299,6 +27424,7 @@ async function runTaishiIssueMode(input, precomputedScan) {
|
|
|
27299
27424
|
const inputBookKey = "bookKey" in input && typeof input.bookKey === "string" && input.bookKey.trim() !== "" ? input.bookKey : void 0;
|
|
27300
27425
|
const scan = precomputedScan ?? (inputBookKey !== void 0 ? await scanTaishiIssueRuns({
|
|
27301
27426
|
bookKey: inputBookKey,
|
|
27427
|
+
...scanProjectRoot === void 0 ? {} : { projectRoot: scanProjectRoot },
|
|
27302
27428
|
...ticketNumber === void 0 ? {} : { ticketNumber }
|
|
27303
27429
|
}) : ticketNumber === void 0 ? await scanTaishiIssueRuns({ projectRoot }) : await scanTaishiIssueRuns({ projectRoot, ticketNumber }));
|
|
27304
27430
|
const issueNumber = "issueNumber" in input ? input.issueNumber : void 0;
|
|
@@ -27386,13 +27512,14 @@ async function runTaishi(input) {
|
|
|
27386
27512
|
if (input.mode === "cohort") {
|
|
27387
27513
|
const ledgerHome = resolveActivationLedgerHome();
|
|
27388
27514
|
return runTaishiCohortMode(ledgerHome, input, async ({ projectRoot, issueNumber, bookKey }) => {
|
|
27389
|
-
const realBookKey = bookKey !== void 0 && !bookKey
|
|
27515
|
+
const realBookKey = bookKey !== void 0 && !isSyntheticTaishiBookKey(bookKey) ? bookKey : void 0;
|
|
27390
27516
|
const ensured = await readOrComputeTaishiIssuePage({
|
|
27391
27517
|
mode: "issue",
|
|
27392
27518
|
projectRoot,
|
|
27393
27519
|
issueNumber,
|
|
27520
|
+
ticketNumber: issueNumber,
|
|
27394
27521
|
...realBookKey === void 0 ? {} : { bookKey: realBookKey }
|
|
27395
|
-
});
|
|
27522
|
+
}, { scanProjectRoot: projectRoot });
|
|
27396
27523
|
return ensured.page;
|
|
27397
27524
|
});
|
|
27398
27525
|
}
|
|
@@ -27407,11 +27534,11 @@ var init_taishi_entry = __esm({
|
|
|
27407
27534
|
"use strict";
|
|
27408
27535
|
init_build();
|
|
27409
27536
|
init_activation_ledger_topology();
|
|
27537
|
+
init_taishi_book_key();
|
|
27410
27538
|
init_taishi_cohort();
|
|
27411
27539
|
init_taishi_ledger();
|
|
27412
27540
|
init_taishi_index();
|
|
27413
27541
|
init_taishi_model_groups();
|
|
27414
|
-
init_activation_ledger_git();
|
|
27415
27542
|
init_taishi_page();
|
|
27416
27543
|
TaishiIssueComputeError = class extends Error {
|
|
27417
27544
|
code = "taishi-issue-compute-failed";
|
|
@@ -27456,7 +27583,7 @@ var init_taishi_entry = __esm({
|
|
|
27456
27583
|
|
|
27457
27584
|
// src/public-cli/taishi-run.ts
|
|
27458
27585
|
import { readFile as readFile16 } from "node:fs/promises";
|
|
27459
|
-
import { isAbsolute as
|
|
27586
|
+
import { isAbsolute as isAbsolute6, resolve as resolve7 } from "node:path";
|
|
27460
27587
|
function resolveTaishiIssueBookKeyFromCwd(cwd = process.cwd()) {
|
|
27461
27588
|
try {
|
|
27462
27589
|
return resolveBookKeyFromGit(cwd);
|
|
@@ -27505,7 +27632,7 @@ async function buildTaishiSweepModeInputFromAttachmentPaths(attachmentPaths) {
|
|
|
27505
27632
|
);
|
|
27506
27633
|
}
|
|
27507
27634
|
const sourcePath = attachmentPaths[0];
|
|
27508
|
-
const absolute =
|
|
27635
|
+
const absolute = isAbsolute6(sourcePath) ? sourcePath : resolve7(sourcePath);
|
|
27509
27636
|
let bytes;
|
|
27510
27637
|
try {
|
|
27511
27638
|
bytes = await readFile16(absolute);
|
|
@@ -27547,9 +27674,26 @@ async function runPublicTaishi(argv, _env, io, parseTaishiArgv2) {
|
|
|
27547
27674
|
return { exitCode: 0 };
|
|
27548
27675
|
}
|
|
27549
27676
|
if (parsed.query === "cohort") {
|
|
27677
|
+
let defaultBookKey;
|
|
27678
|
+
const resolveIssue = (token) => {
|
|
27679
|
+
if (token.kind === "book-qualified") {
|
|
27680
|
+
return { bookKey: token.bookKey, issueNumber: token.issueNumber };
|
|
27681
|
+
}
|
|
27682
|
+
defaultBookKey ??= resolveTaishiIssueBookKeyFromCwd();
|
|
27683
|
+
return { bookKey: defaultBookKey, issueNumber: token.issueNumber };
|
|
27684
|
+
};
|
|
27550
27685
|
const result3 = await runTaishi({
|
|
27551
27686
|
mode: "cohort",
|
|
27552
|
-
groups:
|
|
27687
|
+
groups: [
|
|
27688
|
+
{
|
|
27689
|
+
groupLabel: parsed.groups[0].groupLabel,
|
|
27690
|
+
issues: parsed.groups[0].issues.map(resolveIssue)
|
|
27691
|
+
},
|
|
27692
|
+
{
|
|
27693
|
+
groupLabel: parsed.groups[1].groupLabel,
|
|
27694
|
+
issues: parsed.groups[1].issues.map(resolveIssue)
|
|
27695
|
+
}
|
|
27696
|
+
]
|
|
27553
27697
|
});
|
|
27554
27698
|
io.stdout(`${JSON.stringify(result3, null, 2)}
|
|
27555
27699
|
`);
|
|
@@ -27814,7 +27958,7 @@ function renderHelp() {
|
|
|
27814
27958
|
const doc = helpDocument();
|
|
27815
27959
|
const top = projectCommandHelp("top");
|
|
27816
27960
|
const lines = [
|
|
27817
|
-
`ak-role \u2014 ${top
|
|
27961
|
+
`ak-role \u2014 ${top.summary}`
|
|
27818
27962
|
];
|
|
27819
27963
|
appendUsageAndExamples(lines, "top");
|
|
27820
27964
|
lines.push("", PUBLIC_NAVIGATOR_HELP_NOTE);
|
|
@@ -27865,8 +28009,8 @@ function renderCommandHelp(command) {
|
|
|
27865
28009
|
lines.push(`ak-role ${match.name}`);
|
|
27866
28010
|
}
|
|
27867
28011
|
appendUsageAndExamples(lines, command);
|
|
27868
|
-
if (command in PUBLIC_ROLE_ARGV
|
|
27869
|
-
const owner = command
|
|
28012
|
+
if (command in PUBLIC_ROLE_ARGV) {
|
|
28013
|
+
const owner = command;
|
|
27870
28014
|
lines.push("", "OPTIONS");
|
|
27871
28015
|
lines.push(...renderHumanOwnerOptionLines(owner));
|
|
27872
28016
|
}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ const GIT_DISCOVERY_ENV_KEYS = [
|
|
|
11
11
|
] as const;
|
|
12
12
|
|
|
13
13
|
function envWithoutGitDiscovery(base: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
14
|
-
const env: NodeJS.ProcessEnv = { ...base };
|
|
14
|
+
const env: NodeJS.ProcessEnv = { ...base, LC_ALL: "C" };
|
|
15
15
|
for (const key of GIT_DISCOVERY_ENV_KEYS) {
|
|
16
16
|
delete env[key];
|
|
17
17
|
}
|
|
@@ -19,17 +19,42 @@ function envWithoutGitDiscovery(base: NodeJS.ProcessEnv = process.env): NodeJS.P
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* #413 r2 U5 — single failure-classification owner. A git child nonzero exit is
|
|
23
|
+
* only a *confirmed* non-repository verdict when git's own diagnostic says so
|
|
24
|
+
* ("fatal: not a git repository …", forced to the C locale so the wording is
|
|
25
|
+
* deterministic). Every other diagnostic — dubious ownership exit 128,
|
|
26
|
+
* permissions, anything unknown — leaves the question open: git found or
|
|
27
|
+
* refuses to adjudicate a repository-shaped situation without certifying
|
|
28
|
+
* "non repository". Classification by diagnostic identity, not by exit code
|
|
29
|
+
* alone, because both faces share exit 128.
|
|
30
|
+
*/
|
|
31
|
+
const CONFIRMED_NON_REPOSITORY_STDERR = /^fatal:\s*not a git repository/i;
|
|
32
|
+
|
|
33
|
+
export function isConfirmedNonRepositoryStderr(stderr: string): boolean {
|
|
34
|
+
return CONFIRMED_NON_REPOSITORY_STDERR.test(stderr);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Typed book-key discovery failure: a git child ran and exited nonzero.
|
|
39
|
+
* Original git cause (nonzero exit) is retained. Spawn/OS failures never become
|
|
40
|
+
* this type. `confirmedNonRepository` records whether git itself certified
|
|
41
|
+
* "no repository here" (true) or merely failed for an unadjudicated reason
|
|
42
|
+
* such as dubious ownership (false) — consumers may only synthesize fallback
|
|
43
|
+
* identities from the confirmed face.
|
|
24
44
|
*/
|
|
25
45
|
export class ActivationGitRepositoryRequiredError extends Error {
|
|
26
46
|
readonly code = "AK_ACTIVATION_GIT_REPOSITORY_REQUIRED" as const;
|
|
27
|
-
|
|
47
|
+
readonly confirmedNonRepository: boolean;
|
|
48
|
+
constructor(
|
|
49
|
+
detail: string,
|
|
50
|
+
options?: { cause?: unknown; confirmedNonRepository?: boolean },
|
|
51
|
+
) {
|
|
28
52
|
super(
|
|
29
53
|
`Workflow role activation requires a git repository cwd (git rev-parse --git-common-dir failed): ${detail || "unknown git error"}`,
|
|
30
54
|
options?.cause === undefined ? undefined : { cause: options.cause },
|
|
31
55
|
);
|
|
32
56
|
this.name = "ActivationGitRepositoryRequiredError";
|
|
57
|
+
this.confirmedNonRepository = options?.confirmedNonRepository ?? false;
|
|
33
58
|
}
|
|
34
59
|
}
|
|
35
60
|
|
|
@@ -79,7 +104,10 @@ export function resolveBookKeyFromGit(cwd: string): string {
|
|
|
79
104
|
: typeof err.message === "string"
|
|
80
105
|
? err.message
|
|
81
106
|
: "";
|
|
82
|
-
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
107
|
+
throw new ActivationGitRepositoryRequiredError(detail || "unknown git error", {
|
|
108
|
+
cause: error,
|
|
109
|
+
confirmedNonRepository: isConfirmedNonRepositoryStderr(detail),
|
|
110
|
+
});
|
|
83
111
|
}
|
|
84
112
|
if (commonDir.length === 0) {
|
|
85
113
|
throw new Error("git rev-parse --git-common-dir returned an empty path");
|