@mawaru/sdk 0.21.0 → 0.23.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.
@@ -31,24 +31,6 @@ describe("waitConfigSchema", () => {
31
31
  }
32
32
  })
33
33
 
34
- it("until: ISO 日時(Z / オフセット)を受け付ける", () => {
35
- for (const until_at of [
36
- "2026-07-10T09:00:00.000Z",
37
- "2026-07-10T09:00:00+09:00",
38
- ]) {
39
- expect(waitConfigSchema.parse({ wait_type: "until", until_at })).toEqual({
40
- wait_type: "until",
41
- until_at,
42
- })
43
- }
44
- })
45
-
46
- it("until: 日時でない文字列を拒否する", () => {
47
- expect(() =>
48
- waitConfigSchema.parse({ wait_type: "until", until_at: "2026-07-10" }),
49
- ).toThrow()
50
- })
51
-
52
34
  it("cron: 非空の式を受け付ける(式の妥当性検証は保存時)", () => {
53
35
  expect(
54
36
  waitConfigSchema.parse({ wait_type: "cron", cron_expr: "0 9 * * 1-5" }),
@@ -62,20 +44,26 @@ describe("waitConfigSchema", () => {
62
44
  })
63
45
 
64
46
  it("種別と値カラムの取り違えを拒否する", () => {
65
- // duration に until_at だけ、など判別後の必須欠落
47
+ // duration に cron_expr だけ、など判別後の必須欠落
66
48
  expect(() =>
67
49
  waitConfigSchema.parse({
68
50
  wait_type: "duration",
69
- until_at: "2026-07-10T09:00:00.000Z",
51
+ cron_expr: "0 9 * * *",
70
52
  }),
71
53
  ).toThrow()
72
54
  expect(() =>
73
- waitConfigSchema.parse({ wait_type: "until", duration_seconds: 60 }),
55
+ waitConfigSchema.parse({ wait_type: "cron", duration_seconds: 60 }),
74
56
  ).toThrow()
75
57
  })
76
58
 
77
- it("未知の wait_type を拒否する", () => {
59
+ it("未知の wait_type を拒否する(廃止した until を含む)", () => {
78
60
  expect(() => waitConfigSchema.parse({ wait_type: "forever" })).toThrow()
61
+ expect(() =>
62
+ waitConfigSchema.parse({
63
+ wait_type: "until",
64
+ until_at: "2026-07-10T09:00:00.000Z",
65
+ }),
66
+ ).toThrow()
79
67
  })
80
68
  })
81
69
 
package/_schemas/graph.ts CHANGED
@@ -78,17 +78,15 @@ export const graphNodeKindSchema = nodeKindSchema.extract([
78
78
  export type GraphNodeKind = z.infer<typeof graphNodeKindSchema>
79
79
 
80
80
  // Wait ノードの kind 別設定(node_waits の wait_type に対応する判別ユニオン)。
81
- // duration=経過秒、until=絶対時刻(ISO)、cron=cron 式(次回発火まで1回待つ)。
81
+ // duration=経過秒、cron=cron 式(次に来る該当時刻まで1回待つ)。
82
+ // wait は到達時に fireAt を1つ決めて1回だけ発火するので、絶対時刻を1点指す種別(旧 until)は
83
+ // 繰り返し回るループの部品にならない=持たない。
82
84
  // cron 式の妥当性は保存時に backend の cron パーサで検証する(ここは非空のみ)
83
85
  export const waitConfigSchema = z.discriminatedUnion("wait_type", [
84
86
  z.object({
85
87
  wait_type: z.literal("duration"),
86
88
  duration_seconds: z.number().int().positive(),
87
89
  }),
88
- z.object({
89
- wait_type: z.literal("until"),
90
- until_at: z.iso.datetime({ offset: true }),
91
- }),
92
90
  z.object({
93
91
  wait_type: z.literal("cron"),
94
92
  cron_expr: z.string().min(1),
@@ -272,12 +270,10 @@ export const graphNodeSchema = z.object({
272
270
  icon: nodeIconSchema.optional(),
273
271
  // Slack ビュー(全 kind 共通の任意設定。承認待ち・完了メッセージの見せ方)。
274
272
  // human の view と同じ「不透明なキー」で、実在・可視性の検証は保存時に mawaru 側が行う
275
- // (カタログは非公開。@mawaru/common の slack-view.ts)。省略時は従来表示。
273
+ // (カタログは非公開。@mawaru/common の slack-view.ts)。
274
+ // 省略=承認待ちは承認ボタンだけ・完了は ✅ の1行(データは出さない)。
276
275
  // ポートの型には一切影響しない
277
276
  slack_view: z.string().min(1).optional(),
278
- // Slack の完了メッセージにデータを残すか(false = 1行に畳む)。
279
- // 省略時は defaultSlackKeepData(kind) が入る
280
- slack_keep_data: z.boolean().optional(),
281
277
  // kind 別設定(自 kind のときだけ意味を持つ。省略時は保存側がデフォルトを補う)
282
278
  wait: waitConfigSchema.optional(),
283
279
  program: programConfigSchema.optional(),
@@ -287,13 +283,6 @@ export const graphNodeSchema = z.object({
287
283
  })
288
284
  export type GraphNode = z.infer<typeof graphNodeSchema>
289
285
 
290
- // slack_keep_data の省略時の値。エディタのノード雛形と graph 保存の両方がここを見る
291
- // (DB には常に具体値が入り、「既定に従う」という状態は persist しない)。
292
- // 構造ノードは run の入力・結果そのものなので残し、それ以外は畳む:中間ノードの完了まで
293
- // データで埋まるとスレッドが読めなくなる
294
- export const defaultSlackKeepData = (kind: string): boolean =>
295
- kind === "start" || kind === "end"
296
-
297
286
  // 手動ルートの折れ点(loop 座標系の絶対座標)。connection の waypoints に並べる
298
287
  // (docs/tasks/wip/connection線ルートの手動編集(ウェイポイント).md)
299
288
  export const waypointSchema = z.object({ x: z.number(), y: z.number() })
@@ -9,6 +9,7 @@ export declare const recordedCwds: (transcript: string) => {
9
9
  all: string[];
10
10
  last: string | null;
11
11
  };
12
+ export declare const workspaceRepoName: (cwds: string[]) => string | null;
12
13
  export declare const findProjectDir: (cwd: string) => string | null;
13
14
  export type TranscriptRef = {
14
15
  url: string;
@@ -50,6 +50,19 @@ export const recordedCwds = (transcript) => {
50
50
  }
51
51
  return { all, last };
52
52
  };
53
+ // ランナー上の cwd は /home/runner/work/<実行repo>/<実行repo>/workspace/<対象repo>/... の形。
54
+ // エージェントが実際に触ったのは setup.sh が workspace/ 配下に clone した**対象 repo** なので、
55
+ // そのセグメントだけを人間への手がかりとして取り出す(フルパスはローカルでは解決しないので見せない)
56
+ export const workspaceRepoName = (cwds) => {
57
+ for (const cwd of cwds) {
58
+ const segments = cwd.split("/");
59
+ const index = segments.indexOf("workspace");
60
+ const name = index >= 0 ? segments[index + 1] : undefined;
61
+ if (name)
62
+ return name;
63
+ }
64
+ return null;
65
+ };
53
66
  const projectsRoot = () => join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "projects");
54
67
  // 置き先のディレクトリを **規則を再実装せず観測で決める**(agent-runner の
55
68
  // _helpers/session.ts と同じ方針)。~/.claude/projects/*/*.jsonl の cwd を読み、
@@ -1,3 +1 @@
1
- export declare const runSessionResume: (stepUrl: string | undefined, options: {
2
- yes: boolean;
3
- }) => Promise<number>;
1
+ export declare const runSessionResume: (stepUrl: string | undefined) => Promise<number>;
@@ -1,25 +1,22 @@
1
- import { createInterface } from "node:readline/promises";
1
+ import { homedir } from "node:os";
2
+ import { dirname } from "node:path";
2
3
  import { requireCredentials } from "./credentials.js";
3
- import { downloadTranscript, fetchTranscriptRef, parseStepUrl, placeTranscript, recordedCwds, spawnClaudeResume, } from "./resumeSession.js";
4
- const confirm = async (question) => {
5
- const rl = createInterface({ input: process.stdin, output: process.stdout });
6
- try {
7
- const answer = await rl.question(`${question} [y/N] `);
8
- return /^y(es)?$/i.test(answer.trim());
9
- }
10
- finally {
11
- rl.close();
12
- }
4
+ import { downloadTranscript, fetchTranscriptRef, parseStepUrl, placeTranscript, recordedCwds, spawnClaudeResume, workspaceRepoName, } from "./resumeSession.js";
5
+ // ホーム配下は ~ に畳む(置き先は必ずホーム配下なので実質いつも効く)
6
+ const tilde = (path) => {
7
+ const home = homedir();
8
+ return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
13
9
  };
14
- // `mawaru session resume <step URL> [--yes]`
10
+ // `mawaru session resume <step URL>`
15
11
  //
16
- // 取得 作業ディレクトリの確認 → 配置 → `claude --resume`。
17
- // **どこに cd すべきかは mawaru には分からない**(AI ノードの実行 repo と、エージェントが
18
- // 実際に触った repo は別物で、後者は setup.sh workspace/ 配下に clone した対象 repo)。
19
- // なので推定せず、記録された cwd をそのまま見せて人間に判断させる
20
- export const runSessionResume = async (stepUrl, options) => {
12
+ // **確認は取らない。** 書くのは ~/.claude/projects/<現在地>/<sessionId>.jsonl の新規1ファイルだけで
13
+ // repo には触れないので、場所を間違えても別プロジェクトの履歴に混ざるだけ=やり直せる。
14
+ // **どこに cd すべきかは mawaru には分からない**(AI ノードの実行 repo と、エージェントが実際に
15
+ // 触った repo は別物で、後者は setup.sh が workspace/ 配下に clone した対象 repo)ので推定して
16
+ // 移動もしない。手がかりとして対象 repo 名だけ見せ、現在地と食い違うときに警告する
17
+ export const runSessionResume = async (stepUrl) => {
21
18
  if (!stepUrl) {
22
- console.error("使い方: mawaru session resume <step URL> [--yes]\n" +
19
+ console.error("使い方: mawaru session resume <step URL>\n" +
23
20
  "run 詳細の AI ノードにあるコピーボタンで URL を取得できます。");
24
21
  return 1;
25
22
  }
@@ -28,28 +25,19 @@ export const runSessionResume = async (stepUrl, options) => {
28
25
  const credentials = requireCredentials();
29
26
  const transcriptRef = await fetchTranscriptRef(credentials, ref);
30
27
  const transcript = await downloadTranscript(transcriptRef.url);
31
- console.log(`取得: セッション ${transcriptRef.session_id}`);
28
+ console.log(`取得: セッション ${transcriptRef.session_id.slice(0, 8)}`);
32
29
  if (transcriptRef.redacted) {
33
30
  console.log(`(退避時に secret を ${transcriptRef.redacted} 箇所マスクしています)`);
34
31
  }
35
- const { all, last } = recordedCwds(transcript);
36
32
  const cwd = process.cwd();
37
- if (all.length > 0) {
38
- console.log("このセッションの作業ディレクトリ:");
39
- for (const dir of all) {
40
- console.log(` ${dir}${dir === last ? "(最後に居た場所)" : ""}`);
33
+ const repo = workspaceRepoName(recordedCwds(transcript).all);
34
+ if (repo) {
35
+ console.log(`実行時の作業対象: ${repo}(ランナー上の workspace/${repo})`);
36
+ if (!cwd.includes(repo)) {
37
+ console.log(`⚠ 現在地に ${repo} が含まれません。別 repo で再開しようとしているかもしれません(このまま続けます)`);
41
38
  }
42
39
  }
43
- console.log(`現在地:\n ${cwd}`);
44
- if (!all.includes(cwd)) {
45
- console.log("※ 記録された作業ディレクトリとは違います。記録上の絶対パスはここでは解決しません" +
46
- "(エージェントは読めないパスに当たれば自分で見つけ直します)。");
47
- }
48
- if (!options.yes && !(await confirm("続けますか?"))) {
49
- console.log("中止しました。");
50
- return 1;
51
- }
52
40
  const file = placeTranscript(cwd, transcriptRef.session_id, transcript);
53
- console.log(`配置: ${file}\n再開します…`);
41
+ console.log(`復元先: ${tilde(dirname(file))}\n再開します…`);
54
42
  return await spawnClaudeResume(transcriptRef.session_id);
55
43
  };
@@ -18,20 +18,17 @@ export declare const editableNodeKindSchema: z.ZodEnum<{
18
18
  export type EditableNodeKind = z.infer<typeof editableNodeKindSchema>;
19
19
  export declare const graphNodeKindSchema: z.ZodEnum<{
20
20
  start: "start";
21
- end: "end";
22
21
  ai: "ai";
23
22
  human: "human";
24
23
  program: "program";
25
24
  wait: "wait";
25
+ end: "end";
26
26
  component: "component";
27
27
  }>;
28
28
  export type GraphNodeKind = z.infer<typeof graphNodeKindSchema>;
29
29
  export declare const waitConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
30
30
  wait_type: z.ZodLiteral<"duration">;
31
31
  duration_seconds: z.ZodNumber;
32
- }, z.core.$strip>, z.ZodObject<{
33
- wait_type: z.ZodLiteral<"until">;
34
- until_at: z.ZodISODateTime;
35
32
  }, z.core.$strip>, z.ZodObject<{
36
33
  wait_type: z.ZodLiteral<"cron">;
37
34
  cron_expr: z.ZodString;
@@ -58,8 +55,8 @@ export declare const humanConfigSchema: z.ZodObject<{
58
55
  round_robin: "round_robin";
59
56
  }>>;
60
57
  approval_mode: z.ZodDefault<z.ZodEnum<{
61
- all: "all";
62
58
  any: "any";
59
+ all: "all";
63
60
  }>>;
64
61
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
65
62
  }, z.core.$strip>;
@@ -131,11 +128,11 @@ export declare const graphNodeSchema: z.ZodObject<{
131
128
  id: z.ZodUUID;
132
129
  kind: z.ZodEnum<{
133
130
  start: "start";
134
- end: "end";
135
131
  ai: "ai";
136
132
  human: "human";
137
133
  program: "program";
138
134
  wait: "wait";
135
+ end: "end";
139
136
  component: "component";
140
137
  }>;
141
138
  name: z.ZodString;
@@ -174,13 +171,9 @@ export declare const graphNodeSchema: z.ZodObject<{
174
171
  image_url: z.ZodURL;
175
172
  }, z.core.$strict>]>>;
176
173
  slack_view: z.ZodOptional<z.ZodString>;
177
- slack_keep_data: z.ZodOptional<z.ZodBoolean>;
178
174
  wait: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
179
175
  wait_type: z.ZodLiteral<"duration">;
180
176
  duration_seconds: z.ZodNumber;
181
- }, z.core.$strip>, z.ZodObject<{
182
- wait_type: z.ZodLiteral<"until">;
183
- until_at: z.ZodISODateTime;
184
177
  }, z.core.$strip>, z.ZodObject<{
185
178
  wait_type: z.ZodLiteral<"cron">;
186
179
  cron_expr: z.ZodString;
@@ -201,8 +194,8 @@ export declare const graphNodeSchema: z.ZodObject<{
201
194
  round_robin: "round_robin";
202
195
  }>>;
203
196
  approval_mode: z.ZodDefault<z.ZodEnum<{
204
- all: "all";
205
197
  any: "any";
198
+ all: "all";
206
199
  }>>;
207
200
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
208
201
  }, z.core.$strip>;
@@ -213,7 +206,6 @@ export declare const graphNodeSchema: z.ZodObject<{
213
206
  }, z.core.$strip>>;
214
207
  }, z.core.$strip>;
215
208
  export type GraphNode = z.infer<typeof graphNodeSchema>;
216
- export declare const defaultSlackKeepData: (kind: string) => boolean;
217
209
  export declare const waypointSchema: z.ZodObject<{
218
210
  x: z.ZodNumber;
219
211
  y: z.ZodNumber;
@@ -233,11 +225,11 @@ export declare const saveGraphBodySchema: z.ZodObject<{
233
225
  id: z.ZodUUID;
234
226
  kind: z.ZodEnum<{
235
227
  start: "start";
236
- end: "end";
237
228
  ai: "ai";
238
229
  human: "human";
239
230
  program: "program";
240
231
  wait: "wait";
232
+ end: "end";
241
233
  component: "component";
242
234
  }>;
243
235
  name: z.ZodString;
@@ -276,13 +268,9 @@ export declare const saveGraphBodySchema: z.ZodObject<{
276
268
  image_url: z.ZodURL;
277
269
  }, z.core.$strict>]>>;
278
270
  slack_view: z.ZodOptional<z.ZodString>;
279
- slack_keep_data: z.ZodOptional<z.ZodBoolean>;
280
271
  wait: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
281
272
  wait_type: z.ZodLiteral<"duration">;
282
273
  duration_seconds: z.ZodNumber;
283
- }, z.core.$strip>, z.ZodObject<{
284
- wait_type: z.ZodLiteral<"until">;
285
- until_at: z.ZodISODateTime;
286
274
  }, z.core.$strip>, z.ZodObject<{
287
275
  wait_type: z.ZodLiteral<"cron">;
288
276
  cron_expr: z.ZodString;
@@ -303,8 +291,8 @@ export declare const saveGraphBodySchema: z.ZodObject<{
303
291
  round_robin: "round_robin";
304
292
  }>>;
305
293
  approval_mode: z.ZodDefault<z.ZodEnum<{
306
- all: "all";
307
294
  any: "any";
295
+ all: "all";
308
296
  }>>;
309
297
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
310
298
  }, z.core.$strip>;
@@ -53,17 +53,15 @@ export const graphNodeKindSchema = nodeKindSchema.extract([
53
53
  "component",
54
54
  ]);
55
55
  // Wait ノードの kind 別設定(node_waits の wait_type に対応する判別ユニオン)。
56
- // duration=経過秒、until=絶対時刻(ISO)、cron=cron 式(次回発火まで1回待つ)。
56
+ // duration=経過秒、cron=cron 式(次に来る該当時刻まで1回待つ)。
57
+ // wait は到達時に fireAt を1つ決めて1回だけ発火するので、絶対時刻を1点指す種別(旧 until)は
58
+ // 繰り返し回るループの部品にならない=持たない。
57
59
  // cron 式の妥当性は保存時に backend の cron パーサで検証する(ここは非空のみ)
58
60
  export const waitConfigSchema = z.discriminatedUnion("wait_type", [
59
61
  z.object({
60
62
  wait_type: z.literal("duration"),
61
63
  duration_seconds: z.number().int().positive(),
62
64
  }),
63
- z.object({
64
- wait_type: z.literal("until"),
65
- until_at: z.iso.datetime({ offset: true }),
66
- }),
67
65
  z.object({
68
66
  wait_type: z.literal("cron"),
69
67
  cron_expr: z.string().min(1),
@@ -216,12 +214,10 @@ export const graphNodeSchema = z.object({
216
214
  icon: nodeIconSchema.optional(),
217
215
  // Slack ビュー(全 kind 共通の任意設定。承認待ち・完了メッセージの見せ方)。
218
216
  // human の view と同じ「不透明なキー」で、実在・可視性の検証は保存時に mawaru 側が行う
219
- // (カタログは非公開。@mawaru/common の slack-view.ts)。省略時は従来表示。
217
+ // (カタログは非公開。@mawaru/common の slack-view.ts)。
218
+ // 省略=承認待ちは承認ボタンだけ・完了は ✅ の1行(データは出さない)。
220
219
  // ポートの型には一切影響しない
221
220
  slack_view: z.string().min(1).optional(),
222
- // Slack の完了メッセージにデータを残すか(false = 1行に畳む)。
223
- // 省略時は defaultSlackKeepData(kind) が入る
224
- slack_keep_data: z.boolean().optional(),
225
221
  // kind 別設定(自 kind のときだけ意味を持つ。省略時は保存側がデフォルトを補う)
226
222
  wait: waitConfigSchema.optional(),
227
223
  program: programConfigSchema.optional(),
@@ -229,11 +225,6 @@ export const graphNodeSchema = z.object({
229
225
  human: humanConfigSchema.optional(),
230
226
  component: componentConfigSchema.optional(),
231
227
  });
232
- // slack_keep_data の省略時の値。エディタのノード雛形と graph 保存の両方がここを見る
233
- // (DB には常に具体値が入り、「既定に従う」という状態は persist しない)。
234
- // 構造ノードは run の入力・結果そのものなので残し、それ以外は畳む:中間ノードの完了まで
235
- // データで埋まるとスレッドが読めなくなる
236
- export const defaultSlackKeepData = (kind) => kind === "start" || kind === "end";
237
228
  // 手動ルートの折れ点(loop 座標系の絶対座標)。connection の waypoints に並べる
238
229
  // (docs/tasks/wip/connection線ルートの手動編集(ウェイポイント).md)
239
230
  export const waypointSchema = z.object({ x: z.number(), y: z.number() });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export declare const hookEventSchema: z.ZodEnum<{
3
- input: "input";
4
3
  output: "output";
4
+ input: "input";
5
5
  signal: "signal";
6
6
  }>;
7
7
  export type HookEvent = z.infer<typeof hookEventSchema>;
@@ -9,8 +9,8 @@ export declare const hookManifestSchema: z.ZodObject<{
9
9
  name: z.ZodOptional<z.ZodString>;
10
10
  description: z.ZodOptional<z.ZodString>;
11
11
  on: z.ZodArray<z.ZodEnum<{
12
- input: "input";
13
12
  output: "output";
13
+ input: "input";
14
14
  signal: "signal";
15
15
  }>>;
16
16
  env: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  export declare const nodeKindSchema: z.ZodEnum<{
3
3
  start: "start";
4
- end: "end";
5
4
  ai: "ai";
6
5
  human: "human";
7
6
  program: "program";
8
7
  guardrail: "guardrail";
9
8
  wait: "wait";
9
+ end: "end";
10
10
  component: "component";
11
11
  }>;
12
12
  export type NodeKind = z.infer<typeof nodeKindSchema>;
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ const USAGE = `使い方: mawaru <command> [引数]
15
15
  validate リポジトリ規約を検証する(error があれば exit 1。CI 向け)
16
16
  login ブラウザで承認して mawaru にログインする(~/.mawaru/credentials.json)
17
17
  logout 保存した資格情報を消す
18
- session resume <step URL> [--yes]
18
+ session resume <step URL>
19
19
  AI ノードのセッションを手元に復元して claude --resume で再開する`;
20
20
  const [command, ...rest] = process.argv.slice(2);
21
21
  // init / typegen / validate は repo ルートを任意で受ける(省略時はカレント)
@@ -81,10 +81,10 @@ switch (command) {
81
81
  case "session": {
82
82
  const [subcommand, ...args] = rest;
83
83
  if (subcommand !== "resume") {
84
- fail("使い方: mawaru session resume <step URL> [--yes]");
84
+ fail("使い方: mawaru session resume <step URL>");
85
85
  }
86
86
  try {
87
- const code = await runSessionResume(args.find((a) => !a.startsWith("-")), { yes: args.includes("--yes") || args.includes("-y") });
87
+ const code = await runSessionResume(args.find((a) => !a.startsWith("-")));
88
88
  process.exit(code);
89
89
  }
90
90
  catch (e) {
@@ -52,6 +52,10 @@ skills/<dir>/ # AI ノードが参照する skill(SKILL.md)
52
52
  読める
53
53
  - 実行時はラッパーが Claude Code(headless)を起動し、`outputs` のスキーマに適合する
54
54
  output を検証して報告する。出口が複数あれば AI が1つ選ぶ(複数出口=分岐)
55
+ - **文体の既定は「短く」**:出力の文章は承認画面・Slack・顧客への回答としてそのまま人が読むため、
56
+ ラッパーが枠組みプロンプトで「結論から・前置きや言い換えや締め文句を書かない・1文は短く」を
57
+ 常に指示している。長い文章(詳細なレポート等)が要るノードだけ `prompt` に長さ・文体を明示する
58
+ (指示が既定より優先される)
55
59
 
56
60
  ## フック(`nodes/hook/<dir>/`)
57
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mawaru/sdk",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "mawaru 実行 repo の開発 SDK。契約スキーマ(config.json 規約・ループ graph API)の正 + typegen / validate CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -93,6 +93,16 @@ repo との矛盾・使用中ノードの削除は 409。**graph PUT は冪等
93
93
  6. **報告**:エディタ URL `{MAWARU_APP_URL}/loops/{loopId}/edit` をユーザーに渡し、
94
94
  見た目の確認と run はユーザーに委ねる。
95
95
 
96
+ ## AI ノード:出力は短く(既定)
97
+
98
+ AI ノードの出力の文章は、承認画面・Slack・顧客への回答としてそのまま人が読む。ラッパーの
99
+ 枠組みプロンプトが「結論から・前置きや言い換えや締め文句を書かない・1文は短く」を常に指示して
100
+ いるので、**prompt に「簡潔に」と書き足す必要はない**。逆に長い文章(詳細なレポート等)が要る
101
+ ノードだけ、prompt に長さ・文体を明示する(ノードの指示が既定より優先される)。
102
+
103
+ 出力スキーマも同じ方針で最小限にする:人が確認する値に、使われない補足フィールド
104
+ (説明・根拠・メタ情報)を足さない。
105
+
96
106
  ## AI ノード:決定論的な処理は prompt でなく setup.sh に書く
97
107
 
98
108
  やることが決まっている準備=**外部 repo の clone / skill の持ち込み / 認証キーのファイル化 /