@agentplaneorg/core 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"commit-policy.d.ts","sourceRoot":"","sources":["../../src/commit/commit-policy.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAOlF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,kBAAkB,CAerB"}
1
+ {"version":3,"file":"commit-policy.d.ts","sourceRoot":"","sources":["../../src/commit/commit-policy.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAOlF;AA4BD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,kBAAkB,CAgCrB"}
@@ -15,17 +15,54 @@ export function isGenericSubject(subject, genericTokens) {
15
15
  const tokenSet = new Set(genericTokens.map((t) => t.toLowerCase()));
16
16
  return words.length <= 3 && words.every((w) => tokenSet.has(w));
17
17
  }
18
+ function parseSubjectTemplate(subject) {
19
+ const trimmed = subject.trim();
20
+ if (!trimmed)
21
+ return null;
22
+ const match = /^(\S+)\s+(\S+)\s+(.+)$/.exec(trimmed);
23
+ if (!match)
24
+ return null;
25
+ const emoji = match[1] ?? "";
26
+ const suffix = match[2] ?? "";
27
+ const rest = (match[3] ?? "").trim();
28
+ if (!emoji || !suffix || !rest)
29
+ return null;
30
+ const scopeMatch = /^([a-z][a-z0-9_-]*):\s+(.+)$/.exec(rest);
31
+ if (!scopeMatch)
32
+ return null;
33
+ const scope = scopeMatch[1] ?? "";
34
+ const summary = (scopeMatch[2] ?? "").trim();
35
+ if (!scope || !summary)
36
+ return null;
37
+ return { emoji, suffix, scope, summary };
38
+ }
18
39
  export function validateCommitSubject(opts) {
19
40
  const errors = [];
20
41
  const subject = opts.subject.trim();
21
42
  if (!subject)
22
43
  errors.push("commit subject must be non-empty");
23
44
  const suffix = extractTaskSuffix(opts.taskId);
24
- if (!subject.includes(opts.taskId) && (suffix.length === 0 || !subject.includes(suffix))) {
25
- errors.push("commit subject must include task id or suffix");
45
+ const template = parseSubjectTemplate(subject);
46
+ if (!template) {
47
+ errors.push("commit subject must match: <emoji> <suffix> <scope>: <summary>");
48
+ return { ok: false, errors };
49
+ }
50
+ if (!suffix) {
51
+ errors.push("task id has no suffix");
26
52
  }
27
- if (isGenericSubject(subject, opts.genericTokens)) {
53
+ else if (template.suffix.toLowerCase() !== suffix.toLowerCase()) {
54
+ errors.push("commit subject must include task suffix as the second token");
55
+ }
56
+ const normalizedSummary = stripPunctuation(template.summary).toLowerCase().trim();
57
+ if (!normalizedSummary) {
28
58
  errors.push("commit subject is too generic");
59
+ return { ok: errors.length === 0, errors };
29
60
  }
61
+ const words = normalizedSummary.split(/\s+/).filter(Boolean);
62
+ const tokenSet = new Set(opts.genericTokens.map((t) => t.toLowerCase()));
63
+ const nonGenericCount = words.filter((w) => !tokenSet.has(w)).length;
64
+ // Require at least two words in the summary and at least one non-generic token.
65
+ if (words.length < 2 || nonGenericCount < 1)
66
+ errors.push("commit subject is too generic");
30
67
  return { ok: errors.length === 0, errors };
31
68
  }
@@ -32,6 +32,10 @@ export type AgentplaneConfig = {
32
32
  id_suffix_length_default: number;
33
33
  verify: {
34
34
  required_tags: string[];
35
+ require_steps_for_tags?: string[];
36
+ spike_tag: string;
37
+ enforce_on_plan_approve: boolean;
38
+ enforce_on_start_when_no_plan: boolean;
35
39
  };
36
40
  doc: {
37
41
  sections: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAClD,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,CAAC,CAAC;IAClB,aAAa,EAAE,YAAY,CAAC;IAC5B,oBAAoB,EAAE,kBAAkB,CAAC;IACzC,yBAAyB,EAAE,OAAO,CAAC;IACnC,MAAM,CAAC,EAAE;QACP,SAAS,EAAE;YACT,YAAY,EAAE,OAAO,CAAC;YACtB,eAAe,EAAE,OAAO,CAAC;YACzB,cAAc,EAAE,OAAO,CAAC;SACzB,CAAC;KACH,CAAC;IACF,OAAO,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;KAC7C,CAAC;IACF,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC1D,KAAK,EAAE;QACL,wBAAwB,EAAE,MAAM,CAAC;QACjC,MAAM,EAAE;YAAE,aAAa,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACpC,GAAG,EAAE;YAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACzD,QAAQ,EAAE;YACR,KAAK,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC7C,OAAO,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC/C,QAAQ,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;SACjD,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,aAAa,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,gCAAgC,EAAE,OAAO,CAAC;CAC3C,CAAC;AAEF,wBAAgB,aAAa,IAAI,gBAAgB,CAEhD;AAkED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAa7D;AAID,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B,CAAC;AAQF,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA8B7E;AAkBD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAgBN;AAED,wBAAsB,UAAU,CAC9B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAS3B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAClD,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,CAAC,CAAC;IAClB,aAAa,EAAE,YAAY,CAAC;IAC5B,oBAAoB,EAAE,kBAAkB,CAAC;IACzC,yBAAyB,EAAE,OAAO,CAAC;IACnC,MAAM,CAAC,EAAE;QACP,SAAS,EAAE;YACT,YAAY,EAAE,OAAO,CAAC;YACtB,eAAe,EAAE,OAAO,CAAC;YACzB,cAAc,EAAE,OAAO,CAAC;SACzB,CAAC;KACH,CAAC;IACF,OAAO,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;KAC7C,CAAC;IACF,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC1D,KAAK,EAAE;QACL,wBAAwB,EAAE,MAAM,CAAC;QACjC,MAAM,EAAE;YACN,aAAa,EAAE,MAAM,EAAE,CAAC;YACxB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;YAClC,SAAS,EAAE,MAAM,CAAC;YAClB,uBAAuB,EAAE,OAAO,CAAC;YACjC,6BAA6B,EAAE,OAAO,CAAC;SACxC,CAAC;QACF,GAAG,EAAE;YAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACzD,QAAQ,EAAE;YACR,KAAK,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC7C,OAAO,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC/C,QAAQ,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;SACjD,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,aAAa,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,gCAAgC,EAAE,OAAO,CAAC;CAC3C,CAAC;AAEF,wBAAgB,aAAa,IAAI,gBAAgB,CAEhD;AAkED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAa7D;AAID,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B,CAAC;AAQF,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA8B7E;AAkBD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAgBN;AAED,wBAAsB,UAAU,CAC9B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAS3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-branch.d.ts","sourceRoot":"","sources":["../../src/git/base-branch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiDxD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGzB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlB;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBzB"}
1
+ {"version":3,"file":"base-branch.d.ts","sourceRoot":"","sources":["../../src/git/base-branch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiCxD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGzB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,CAAC,CAIlB;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASzB"}
@@ -2,7 +2,6 @@ import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { resolveProject } from "../project/project-root.js";
4
4
  const execFileAsync = promisify(execFile);
5
- const DEFAULT_BASE_BRANCH = "main";
6
5
  const GIT_CONFIG_BASE_BRANCH_KEY = "agentplane.baseBranch";
7
6
  async function gitConfigGet(cwd, key) {
8
7
  try {
@@ -32,30 +31,15 @@ async function gitConfigUnset(cwd, key) {
32
31
  throw err;
33
32
  }
34
33
  }
35
- async function gitCurrentBranch(cwd) {
36
- try {
37
- const { stdout } = await execFileAsync("git", ["symbolic-ref", "--short", "HEAD"], { cwd });
38
- const trimmed = stdout.trim();
39
- if (trimmed)
40
- return trimmed;
41
- }
42
- catch {
43
- // fall through
44
- }
45
- const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
46
- const trimmed = stdout.trim();
47
- if (!trimmed || trimmed === "HEAD") {
48
- throw new Error("Failed to resolve current branch");
49
- }
50
- return trimmed;
51
- }
52
34
  export async function getPinnedBaseBranch(opts) {
53
35
  const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
54
36
  return await gitConfigGet(resolved.gitRoot, GIT_CONFIG_BASE_BRANCH_KEY);
55
37
  }
56
38
  export async function getBaseBranch(opts) {
57
39
  const pinned = await getPinnedBaseBranch(opts);
58
- return pinned ?? DEFAULT_BASE_BRANCH;
40
+ if (pinned)
41
+ return pinned;
42
+ throw new Error("base branch is not pinned");
59
43
  }
60
44
  export async function setPinnedBaseBranch(opts) {
61
45
  const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
@@ -79,12 +63,5 @@ export async function resolveBaseBranch(opts) {
79
63
  });
80
64
  if (pinned)
81
65
  return pinned;
82
- if (opts.mode === "branch_pr") {
83
- const resolved = await resolveProject({
84
- cwd: opts.cwd,
85
- rootOverride: opts.rootOverride ?? null,
86
- });
87
- return await gitCurrentBranch(resolved.gitRoot);
88
- }
89
66
  return null;
90
67
  }
@@ -6,4 +6,8 @@ export declare function getUnstagedFiles(opts: {
6
6
  cwd: string;
7
7
  rootOverride?: string | null;
8
8
  }): Promise<string[]>;
9
+ export declare function getUnstagedTrackedFiles(opts: {
10
+ cwd: string;
11
+ rootOverride?: string | null;
12
+ }): Promise<string[]>;
9
13
  //# sourceMappingURL=git-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-utils.d.ts","sourceRoot":"","sources":["../../src/git/git-utils.ts"],"names":[],"mappings":"AAeA,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAcpB"}
1
+ {"version":3,"file":"git-utils.d.ts","sourceRoot":"","sources":["../../src/git/git-utils.ts"],"names":[],"mappings":"AAoBA,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAID,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGpB"}
@@ -2,30 +2,33 @@ import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { resolveProject } from "../project/project-root.js";
4
4
  const execFileAsync = promisify(execFile);
5
- async function gitLines(cwd, args) {
6
- const { stdout } = await execFileAsync("git", args, { cwd });
7
- return stdout
8
- .split("\n")
9
- .map((line) => line.trim())
10
- .filter((line) => line.length > 0);
5
+ async function gitNullSeparatedPaths(cwd, args) {
6
+ const { stdout } = await execFileAsync("git", args, {
7
+ cwd,
8
+ encoding: "buffer",
9
+ maxBuffer: 10 * 1024 * 1024,
10
+ });
11
+ const text = Buffer.isBuffer(stdout) ? stdout.toString("utf8") : String(stdout);
12
+ return text
13
+ .split("\0")
14
+ .map((entry) => entry.trim())
15
+ .filter((entry) => entry.length > 0);
11
16
  }
12
17
  export async function getStagedFiles(opts) {
13
18
  const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
14
- return await gitLines(resolved.gitRoot, ["diff", "--name-only", "--cached"]);
19
+ return await gitNullSeparatedPaths(resolved.gitRoot, ["diff", "--name-only", "--cached", "-z"]);
15
20
  }
16
21
  export async function getUnstagedFiles(opts) {
17
22
  const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
18
- const lines = await gitLines(resolved.gitRoot, ["status", "--porcelain"]);
19
- const files = [];
20
- for (const line of lines) {
21
- const status = line.slice(0, 2);
22
- const filePart = line.slice(3).trim();
23
- if (!filePart)
24
- continue;
25
- const name = filePart.includes("->") ? filePart.split("->").at(-1)?.trim() : filePart;
26
- if ((status === "??" || status[1] !== " ") && name) {
27
- files.push(name);
28
- }
29
- }
30
- return files;
23
+ const [unstaged, untracked] = await Promise.all([
24
+ gitNullSeparatedPaths(resolved.gitRoot, ["diff", "--name-only", "-z"]),
25
+ gitNullSeparatedPaths(resolved.gitRoot, ["ls-files", "--others", "--exclude-standard", "-z"]),
26
+ ]);
27
+ return [...new Set([...unstaged, ...untracked])].toSorted((a, b) => a.localeCompare(b));
28
+ }
29
+ // Tracked-only dirty check (ignores untracked files). This matches the repo policy
30
+ // definition of "clean" used by guardrails: `git status --short --untracked-files=no`.
31
+ export async function getUnstagedTrackedFiles(opts) {
32
+ const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
33
+ return await gitNullSeparatedPaths(resolved.gitRoot, ["diff", "--name-only", "-z"]);
31
34
  }
package/dist/index.d.ts CHANGED
@@ -11,5 +11,5 @@ export { buildTasksExportSnapshot, canonicalTasksPayload, canonicalizeJson, comp
11
11
  export { lintTasksFile, lintTasksSnapshot, readTasksExport, type TasksLintResult, } from "./tasks/tasks-lint.js";
12
12
  export { clearPinnedBaseBranch, getBaseBranch, getPinnedBaseBranch, resolveBaseBranch, setPinnedBaseBranch, } from "./git/base-branch.js";
13
13
  export { extractTaskSuffix, isGenericSubject, validateCommitSubject, type CommitPolicyResult, } from "./commit/commit-policy.js";
14
- export { getStagedFiles, getUnstagedFiles } from "./git/git-utils.js";
14
+ export { getStagedFiles, getUnstagedFiles, getUnstagedTrackedFiles } from "./git/git-utils.js";
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -11,4 +11,4 @@ export { buildTasksExportSnapshot, canonicalTasksPayload, canonicalizeJson, comp
11
11
  export { lintTasksFile, lintTasksSnapshot, readTasksExport, } from "./tasks/tasks-lint.js";
12
12
  export { clearPinnedBaseBranch, getBaseBranch, getPinnedBaseBranch, resolveBaseBranch, setPinnedBaseBranch, } from "./git/base-branch.js";
13
13
  export { extractTaskSuffix, isGenericSubject, validateCommitSubject, } from "./commit/commit-policy.js";
14
- export { getStagedFiles, getUnstagedFiles } from "./git/git-utils.js";
14
+ export { getStagedFiles, getUnstagedFiles, getUnstagedTrackedFiles } from "./git/git-utils.js";
@@ -1 +1 @@
1
- {"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAqBF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAYlE;AAkFD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiClF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F"}
1
+ {"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAqBF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAYlE;AA6FD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAuClF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F"}
@@ -46,6 +46,8 @@ export function parseTaskReadme(markdown) {
46
46
  return { frontmatter: parsed, body };
47
47
  }
48
48
  function renderScalar(value) {
49
+ if (value === undefined)
50
+ return "null";
49
51
  if (value === null)
50
52
  return "null";
51
53
  if (typeof value === "string")
@@ -57,11 +59,14 @@ function renderScalar(value) {
57
59
  throw new TypeError(`Unsupported scalar type: ${typeof value}`);
58
60
  }
59
61
  function renderFlowSeq(value) {
60
- const parts = value.map((v) => {
62
+ const parts = value
63
+ .filter((v) => v !== undefined)
64
+ .map((v) => {
61
65
  if (Array.isArray(v))
62
66
  return renderFlowSeq(v);
63
67
  if (isRecord(v))
64
68
  return `{ ${orderedKeys(v, null)
69
+ .filter((k) => v[k] !== undefined)
65
70
  .map((k) => `${k}: ${renderScalar(v[k])}`)
66
71
  .join(", ")} }`;
67
72
  return renderScalar(v);
@@ -73,6 +78,8 @@ function renderMapLines(value, indent, preferredKeyOrder) {
73
78
  const lines = [];
74
79
  for (const k of keys) {
75
80
  const v = value[k];
81
+ if (v === undefined)
82
+ continue;
76
83
  lines.push(...renderValueLines(k, v, indent));
77
84
  }
78
85
  return lines;
@@ -81,6 +88,8 @@ function isStringArray(value) {
81
88
  return value.every((v) => typeof v === "string");
82
89
  }
83
90
  function renderValueLines(key, value, indent) {
91
+ if (value === undefined)
92
+ return [];
84
93
  if (Array.isArray(value)) {
85
94
  if (value.length === 0)
86
95
  return [`${indent}${key}: []`];
@@ -95,7 +104,11 @@ function renderValueLines(key, value, indent) {
95
104
  ...value.flatMap((item) => {
96
105
  if (!isRecord(item))
97
106
  throw new TypeError("Expected an object item in YAML sequence");
98
- const preferred = key === "comments" ? ["author", "body"] : null;
107
+ const preferred = key === "comments"
108
+ ? ["author", "body"]
109
+ : key === "events"
110
+ ? ["type", "at", "author", "from", "to", "state", "note", "body"]
111
+ : null;
99
112
  const itemLines = renderMapLines(item, `${indent} `, preferred);
100
113
  if (itemLines.length === 0)
101
114
  return [`${indent} - {}`];
@@ -124,6 +137,9 @@ export function renderTaskFrontmatter(frontmatter) {
124
137
  const preferredKeyOrder = [
125
138
  "id",
126
139
  "title",
140
+ "result_summary",
141
+ "risk_level",
142
+ "breaking",
127
143
  "status",
128
144
  "priority",
129
145
  "owner",
@@ -137,6 +153,7 @@ export function renderTaskFrontmatter(frontmatter) {
137
153
  "verification",
138
154
  "commit",
139
155
  "comments",
156
+ "events",
140
157
  "doc_version",
141
158
  "doc_updated_at",
142
159
  "doc_updated_by",
@@ -147,7 +164,10 @@ export function renderTaskFrontmatter(frontmatter) {
147
164
  const ordered = orderedKeys(frontmatter, preferredKeyOrder);
148
165
  const lines = [];
149
166
  for (const k of ordered) {
150
- lines.push(...renderValueLines(k, frontmatter[k], ""));
167
+ const value = frontmatter[k];
168
+ if (value === undefined)
169
+ continue;
170
+ lines.push(...renderValueLines(k, value, ""));
151
171
  }
152
172
  return `---\n${lines.join("\n")}\n---\n`;
153
173
  }
@@ -1,6 +1,16 @@
1
1
  import { generateTaskId } from "./task-id.js";
2
2
  export type TaskStatus = "TODO" | "DOING" | "DONE" | "BLOCKED";
3
3
  export type TaskPriority = "low" | "normal" | "med" | "high";
4
+ export type TaskEvent = {
5
+ type: "status" | "comment" | "verify";
6
+ at: string;
7
+ author: string;
8
+ from?: string;
9
+ to?: string;
10
+ state?: string;
11
+ note?: string;
12
+ body?: string;
13
+ };
4
14
  export type TaskFrontmatter = {
5
15
  id: string;
6
16
  title: string;
@@ -26,6 +36,7 @@ export type TaskFrontmatter = {
26
36
  author: string;
27
37
  body: string;
28
38
  }[];
39
+ events?: TaskEvent[];
29
40
  doc_version: 2;
30
41
  doc_updated_at: string;
31
42
  doc_updated_by: string;
@@ -1 +1 @@
1
- {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/tasks/task-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/tasks/task-store.ts"],"names":[],"mappings":"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"}
@@ -158,6 +158,7 @@ export async function createTask(opts) {
158
158
  note: null,
159
159
  },
160
160
  comments: [],
161
+ events: [],
161
162
  doc_version: 2,
162
163
  doc_updated_at: nowIso(),
163
164
  doc_updated_by: opts.owner,
@@ -22,6 +22,16 @@ export type TasksExportTask = {
22
22
  author: string;
23
23
  body: string;
24
24
  }[];
25
+ events?: {
26
+ type: string;
27
+ at: string;
28
+ author: string;
29
+ from?: string;
30
+ to?: string;
31
+ state?: string;
32
+ note?: string;
33
+ body?: string;
34
+ }[];
25
35
  doc_version: 2;
26
36
  doc_updated_at: string;
27
37
  doc_updated_by: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-export.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-export.ts"],"names":[],"mappings":"AAaA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWxD;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,WAAW,EAAE,CAAC,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAGrE;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqE/B;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAW3D"}
1
+ {"version":3,"file":"tasks-export.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-export.ts"],"names":[],"mappings":"AAaA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWxD;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,WAAW,EAAE,CAAC,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAGrE;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAyG/B;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAW3D"}
@@ -55,7 +55,24 @@ export async function buildTasksExportSnapshot(opts) {
55
55
  .filter((c) => typeof c.author === "string" && typeof c.body === "string")
56
56
  .map((c) => ({ author: c.author, body: c.body }))
57
57
  : [];
58
- return {
58
+ const events = Array.isArray(fm.events)
59
+ ? fm.events
60
+ .filter((event) => isRecord(event))
61
+ .filter((event) => typeof event.type === "string" &&
62
+ typeof event.at === "string" &&
63
+ typeof event.author === "string")
64
+ .map((event) => ({
65
+ type: event.type,
66
+ at: event.at,
67
+ author: event.author,
68
+ from: typeof event.from === "string" ? event.from : undefined,
69
+ to: typeof event.to === "string" ? event.to : undefined,
70
+ state: typeof event.state === "string" ? event.state : undefined,
71
+ note: typeof event.note === "string" ? event.note : undefined,
72
+ body: typeof event.body === "string" ? event.body : undefined,
73
+ }))
74
+ : [];
75
+ const base = {
59
76
  id: typeof fm.id === "string" ? fm.id : t.id,
60
77
  title: typeof fm.title === "string" ? fm.title : "",
61
78
  status: typeof fm.status === "string" ? fm.status : "",
@@ -73,6 +90,10 @@ export async function buildTasksExportSnapshot(opts) {
73
90
  dirty: false,
74
91
  id_source: "generated",
75
92
  };
93
+ if (events.length > 0) {
94
+ return { ...base, events };
95
+ }
96
+ return base;
76
97
  });
77
98
  const sorted = exportTasks.toSorted((a, b) => a.id.localeCompare(b.id));
78
99
  const checksum = computeTasksChecksum(sorted);
@@ -3,7 +3,7 @@ import { type TasksExportSnapshot } from "./tasks-export.js";
3
3
  export type TasksLintResult = {
4
4
  errors: string[];
5
5
  };
6
- export declare function lintTasksSnapshot(snapshot: TasksExportSnapshot, config: AgentplaneConfig): TasksLintResult;
6
+ export declare function lintTasksSnapshot(snapshot: TasksExportSnapshot, _config: AgentplaneConfig): TasksLintResult;
7
7
  export declare function readTasksExport(opts: {
8
8
  cwd: string;
9
9
  rootOverride?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-lint.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-lint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AA2DF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,gBAAgB,GACvB,eAAe,CA4GjB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAOrF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,eAAe,CAAC,CAG3B"}
1
+ {"version":3,"file":"tasks-lint.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-lint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AA2DF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,eAAe,CAmGjB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAOrF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,eAAe,CAAC,CAG3B"}
@@ -48,7 +48,7 @@ function hasCycle(dependsOn) {
48
48
  }
49
49
  return null;
50
50
  }
51
- export function lintTasksSnapshot(snapshot, config) {
51
+ export function lintTasksSnapshot(snapshot, _config) {
52
52
  const errors = [];
53
53
  if (!isRecord(snapshot) || !Array.isArray(snapshot.tasks) || !isRecord(snapshot.meta)) {
54
54
  return { errors: ["tasks.json must have { tasks: [], meta: {} }"] };
@@ -127,14 +127,6 @@ export function lintTasksSnapshot(snapshot, config) {
127
127
  typeof t.commit.message !== "string")) {
128
128
  errors.push(`${id}: DONE tasks must have commit {hash,message}`);
129
129
  }
130
- const requiredTags = new Set(config.tasks.verify.required_tags);
131
- const tagList = Array.isArray(t.tags) ? t.tags : [];
132
- const needsVerify = tagList.some((tag) => requiredTags.has(tag));
133
- if (needsVerify) {
134
- const verifyList = Array.isArray(t.verify) ? t.verify : [];
135
- if (verifyList.length === 0)
136
- errors.push(`${id}: verify is required for tags: ${[...requiredTags].join(", ")}`);
137
- }
138
130
  }
139
131
  // Depends-on must reference known tasks (only after tasks parsed).
140
132
  for (const [id, t] of byId.entries()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentplaneorg/core",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "Core utilities and models for the Agent Plane CLI.",
5
5
  "keywords": [
6
6
  "agentplane",
@@ -144,14 +144,27 @@
144
144
  "type": "object",
145
145
  "additionalProperties": true,
146
146
  "required": ["required_tags"],
147
- "default": { "required_tags": ["code", "backend", "frontend"] },
147
+ "default": {
148
+ "required_tags": ["code", "backend", "frontend"],
149
+ "spike_tag": "spike",
150
+ "enforce_on_plan_approve": true,
151
+ "enforce_on_start_when_no_plan": true
152
+ },
148
153
  "properties": {
149
154
  "required_tags": {
150
155
  "type": "array",
151
156
  "items": { "type": "string", "minLength": 1 },
152
157
  "uniqueItems": true,
153
158
  "default": ["code", "backend", "frontend"]
154
- }
159
+ },
160
+ "require_steps_for_tags": {
161
+ "type": "array",
162
+ "items": { "type": "string", "minLength": 1 },
163
+ "uniqueItems": true
164
+ },
165
+ "spike_tag": { "type": "string", "minLength": 1, "default": "spike" },
166
+ "enforce_on_plan_approve": { "type": "boolean", "default": true },
167
+ "enforce_on_start_when_no_plan": { "type": "boolean", "default": true }
155
168
  }
156
169
  },
157
170
  "doc": {