terminal-quest 1.0.2 → 1.1.2

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.
@@ -239,6 +239,7 @@ export const story07 = {
239
239
  missions: [
240
240
  {
241
241
  id: 'mission-07-01',
242
+ goal: '削除前に ls と cat で中身を確認する習慣を身につける',
242
243
  title: '\u3046\u3063\u304B\u308A\u524A\u9664',
243
244
  description: '\u30D5\u30A1\u30A4\u30EB\u3092\u524A\u9664\u3059\u308B\u524D\u306B\u3001\u307E\u305A\u4E2D\u8EAB\u3092\u78BA\u8A8D\u3059\u308B\u7FD2\u6163\u3092\u3064\u3051\u3088\u3046\u3002',
244
245
  narrative: '\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u4E0D\u8981\u306A\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u30D5\u30A1\u30A4\u30EB\u304C\u6B8B\u3063\u3066\u3044\u308B\u3089\u3057\u3044\u3002\u3067\u3082\u524A\u9664\u3059\u308B\u524D\u306B\u3001\u307E\u305A\u4F55\u304C\u3042\u308B\u304B\u78BA\u8A8D\u3057\u3088\u3046\u3002\u91CD\u8981\u306A\u30D5\u30A1\u30A4\u30EB\u3092\u9593\u9055\u3048\u3066\u524A\u9664\u3057\u305F\u3089\u5927\u5909\u3060\uFF01',
@@ -282,6 +283,7 @@ export const story07 = {
282
283
  },
283
284
  {
284
285
  id: 'mission-07-02',
286
+ goal: '削除する前にバックアップを取る安全な運用手順を身につける',
285
287
  title: '\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u306E\u6975\u610F',
286
288
  description: '\u524A\u9664\u3059\u308B\u524D\u306B\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u3092\u53D6\u308B\u7FD2\u6163\u3092\u8EAB\u306B\u3064\u3051\u3088\u3046\u3002',
287
289
  narrative: '\u30B5\u30FC\u30D0\u30FC\u306E\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u6574\u7406\u3059\u308B\u3053\u3068\u306B\u306A\u3063\u305F\u3002\u3067\u3082\u3044\u304D\u306A\u308A\u524A\u9664\u3059\u308B\u306E\u306F\u5371\u967A\u3060\u3002\u307E\u305A\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u3092\u53D6\u3063\u3066\u304B\u3089\u3001\u4E0D\u8981\u306A\u30D5\u30A1\u30A4\u30EB\u3092\u524A\u9664\u3057\u3088\u3046\u3002',
@@ -325,6 +327,7 @@ export const story07 = {
325
327
  },
326
328
  {
327
329
  id: 'mission-07-03',
330
+ goal: 'rm -rf の威力を理解し、正しい対象だけを削除できるようになる',
328
331
  title: 'rm -rf \u306E\u6B63\u4F53',
329
332
  description: 'rm -rf \u306E\u5A01\u529B\u3092\u7406\u89E3\u3057\u3001\u6B63\u3057\u3044\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3060\u3051\u3092\u524A\u9664\u3057\u3088\u3046\u3002',
330
333
  narrative: '\u30D3\u30EB\u30C9\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u6563\u3089\u304B\u3063\u3066\u3044\u308B\u3002\u53E4\u3044\u30D3\u30EB\u30C9\u7D50\u679C\u3092 rm -rf \u3067\u524A\u9664\u3057\u305F\u3044\u304C\u3001\u9593\u9055\u3048\u3066\u30BD\u30FC\u30B9\u30B3\u30FC\u30C9\u3092\u524A\u9664\u3057\u305F\u3089\u53D6\u308A\u8FD4\u3057\u304C\u3064\u304B\u306A\u3044\u3002\u307E\u305A\u4E2D\u8EAB\u3092\u78BA\u8A8D\u3057\u3066\u304B\u3089\u524A\u9664\u3057\u3088\u3046\u3002',
@@ -365,6 +368,7 @@ export const story07 = {
365
368
  },
366
369
  {
367
370
  id: 'mission-07-04',
371
+ goal: 'find で削除対象を事前確認してから安全に削除するワークフローを習得する',
368
372
  title: '\u5B89\u5168\u306A\u7FD2\u6163',
369
373
  description: 'find \u3067\u524A\u9664\u5BFE\u8C61\u3092\u78BA\u8A8D\u3057\u3066\u304B\u3089\u3001\u5B89\u5168\u306B\u524A\u9664\u3057\u3088\u3046\u3002',
370
374
  narrative: '\u30ED\u30B0\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u53E4\u3044\u30ED\u30B0\u30D5\u30A1\u30A4\u30EB\u304C\u6EA2\u308C\u3066\u3044\u308B\u3002\u53E4\u3044\u6708\u6B21\u30ED\u30B0\u3092\u524A\u9664\u3057\u3066\u6574\u7406\u3057\u305F\u3044\u304C\u3001error.log \u3084 access.log \u306F\u6D88\u3057\u3066\u306F\u3044\u3051\u306A\u3044\u3002\u307E\u305A find \u3067\u524A\u9664\u5BFE\u8C61\u3092\u7D5E\u308A\u8FBC\u3082\u3046\u3002',
@@ -378,6 +378,13 @@ export const storyK1 = {
378
378
  id: 'mission-k1-01',
379
379
  title: '冒険のはじまり',
380
380
  description: 'いまいる場所をたしかめて、まわりを見てみよう!',
381
+ goal: 'pwd と ls をつかって、いまいる場所とまわりにあるものがわかるようになる',
382
+ review: {
383
+ question: 'いまいるばしょをひょうじするコマンドはどれかな?',
384
+ choices: ['ls', 'cd', 'pwd'],
385
+ correctIndex: 2,
386
+ explanation: 'pwd はいまいるばしょ(ディレクトリ)をひょうじするコマンドだよ。「Print Working Directory」のりゃくだよ。',
387
+ },
381
388
  narrative: 'きみは冒険の世界にやってきた!スタート地点にいるみたいだけど、ここはどこだろう?まずは「いまいる場所」をたしかめて、まわりに何があるか見てみよう。',
382
389
  initialCwd: '/冒険の世界/スタート地点',
383
390
  initialFS: mission1FS,
@@ -392,6 +399,11 @@ export const storyK1 = {
392
399
  { level: 2, text: '「pwd」っていう3文字のコマンドだよ。' },
393
400
  { level: 3, text: '「pwd」とにゅうりょくして Enter をおしてね。' },
394
401
  ],
402
+ feedbacks: [
403
+ { pattern: 'ls', message: 'ls はまわりを見るコマンドだよ。いまいる場所をしるには、べつのコマンドをつかうよ。' },
404
+ { pattern: 'cd', message: 'cd はべつの場所にいどうするコマンドだよ。いまいる場所をたしかめるには、べつのコマンドをつかうよ。' },
405
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。いまいる場所をしるには、べつのコマンドをつかうよ。' },
406
+ ],
395
407
  },
396
408
  {
397
409
  id: 'obj-k1-01-02',
@@ -402,6 +414,11 @@ export const storyK1 = {
402
414
  { level: 2, text: '「ls」っていう2文字のコマンドだよ。' },
403
415
  { level: 3, text: '「ls」とにゅうりょくして Enter をおしてね。' },
404
416
  ],
417
+ feedbacks: [
418
+ { pattern: 'pwd', message: 'pwd はいまいる場所をひょうじするコマンドだよ。まわりを見るには、べつのコマンドをつかうよ。' },
419
+ { pattern: 'cd', message: 'cd はべつの場所にいどうするコマンドだよ。まわりにあるものを見るには、べつのコマンドをつかうよ。' },
420
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。まわりにあるものを見るには、べつのコマンドをつかうよ。' },
421
+ ],
405
422
  },
406
423
  ],
407
424
  },
@@ -410,6 +427,13 @@ export const storyK1 = {
410
427
  id: 'mission-k1-02',
411
428
  title: '森を冒険しよう',
412
429
  description: '魔法の森にいって、古い巻物を読んでみよう!',
430
+ goal: 'cd でべつの場所にいどうし、cat でファイルを読めるようになる',
431
+ review: {
432
+ question: 'べつのばしょにいどうするコマンドはどれかな?',
433
+ choices: ['pwd', 'ls', 'cd', 'cat'],
434
+ correctIndex: 2,
435
+ explanation: 'cd はべつのばしょ(ディレクトリ)にいどうするコマンドだよ。「Change Directory」のりゃくだよ。',
436
+ },
413
437
  narrative: '案内板に「魔法の森には何かひみつがある」と書いてあった。森にいって、手がかりをさがそう!巻物を読んだら、スタート地点にもどってこよう。',
414
438
  initialCwd: '/冒険の世界/スタート地点',
415
439
  initialFS: mission2FS,
@@ -424,6 +448,11 @@ export const storyK1 = {
424
448
  { level: 2, text: '「cd」のあとに、いきたい場所の名前を書くよ。' },
425
449
  { level: 3, text: '「cd /冒険の世界/魔法の森」とにゅうりょくしてね。' },
426
450
  ],
451
+ feedbacks: [
452
+ { pattern: 'pwd', message: 'pwd はいまいる場所をたしかめるコマンドだよ。べつの場所にいくには、いどうするコマンドをつかうよ。' },
453
+ { pattern: 'ls', message: 'ls はまわりにあるものを見るコマンドだよ。べつの場所にいくには、いどうするコマンドをつかうよ。' },
454
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。べつの場所にいくには、いどうするコマンドをつかうよ。' },
455
+ ],
427
456
  },
428
457
  {
429
458
  id: 'obj-k1-02-02',
@@ -434,6 +463,11 @@ export const storyK1 = {
434
463
  { level: 2, text: '「cat」のあとにファイル名を書くと、中身が読めるよ。' },
435
464
  { level: 3, text: '「cat 古い巻物.txt」とにゅうりょくしてね。' },
436
465
  ],
466
+ feedbacks: [
467
+ { pattern: 'pwd', message: 'pwd はいまいる場所をたしかめるコマンドだよ。ファイルの中身を読むには、べつのコマンドをつかうよ。' },
468
+ { pattern: 'ls', message: 'ls はまわりにあるものを見るコマンドだよ。ファイルの中身を読むには、べつのコマンドをつかうよ。' },
469
+ { pattern: 'cd', message: 'cd はべつの場所にいどうするコマンドだよ。ファイルの中身を読むには、べつのコマンドをつかうよ。' },
470
+ ],
437
471
  },
438
472
  {
439
473
  id: 'obj-k1-02-03',
@@ -444,6 +478,11 @@ export const storyK1 = {
444
478
  { level: 2, text: 'スタート地点のフルパスを指定しよう。' },
445
479
  { level: 3, text: '「cd /冒険の世界/スタート地点」とにゅうりょくしてね。' },
446
480
  ],
481
+ feedbacks: [
482
+ { pattern: 'pwd', message: 'pwd はいまいる場所をたしかめるコマンドだよ。もどるには、いどうするコマンドをつかうよ。' },
483
+ { pattern: 'ls', message: 'ls はまわりにあるものを見るコマンドだよ。もどるには、いどうするコマンドをつかうよ。' },
484
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。もどるには、いどうするコマンドをつかうよ。' },
485
+ ],
447
486
  },
448
487
  ],
449
488
  },
@@ -452,6 +491,13 @@ export const storyK1 = {
452
491
  id: 'mission-k1-03',
453
492
  title: '秘密基地を作ろう',
454
493
  description: 'じぶんだけの秘密基地をつくろう!部屋もつくって、ノートもおこう。',
494
+ goal: 'mkdir でフォルダをつくり、touch でファイルをつくれるようになる',
495
+ review: {
496
+ question: 'あたらしいフォルダをつくるコマンドはどれかな?',
497
+ choices: ['touch', 'mkdir', 'rm', 'ls'],
498
+ correctIndex: 1,
499
+ explanation: 'mkdir はあたらしいフォルダ(ディレクトリ)をつくるコマンドだよ。「Make Directory」のりゃくだよ。',
500
+ },
455
501
  narrative: '冒険には秘密基地がひつようだ!まずは基地をつくって、たからものをしまう部屋と、冒険のきろくをつけるノートを用意しよう。',
456
502
  initialCwd: '/冒険の世界',
457
503
  initialFS: mission3FS,
@@ -466,6 +512,11 @@ export const storyK1 = {
466
512
  { level: 2, text: '「mkdir」のあとにフォルダの名前を書くよ。' },
467
513
  { level: 3, text: '「mkdir 秘密基地」とにゅうりょくしてね。' },
468
514
  ],
515
+ feedbacks: [
516
+ { pattern: 'touch', message: 'touch はからっぽのファイルをつくるコマンドだよ。フォルダをつくるには、べつのコマンドをつかうよ。' },
517
+ { pattern: 'ls', message: 'ls はまわりにあるものを見るコマンドだよ。あたらしいフォルダをつくるには、べつのコマンドをつかうよ。' },
518
+ { pattern: 'cd', message: 'cd はべつの場所にいどうするコマンドだよ。あたらしいフォルダをつくるには、べつのコマンドをつかうよ。' },
519
+ ],
469
520
  },
470
521
  {
471
522
  id: 'obj-k1-03-02',
@@ -476,6 +527,10 @@ export const storyK1 = {
476
527
  { level: 2, text: '「mkdir」でパスを指定して、中にフォルダがつくれるよ。' },
477
528
  { level: 3, text: '「mkdir 秘密基地/宝物庫」とにゅうりょくしてね。' },
478
529
  ],
530
+ feedbacks: [
531
+ { pattern: 'touch', message: 'touch はファイルをつくるコマンドだよ。フォルダをつくるには、さっきとおなじコマンドをつかうよ。' },
532
+ { pattern: 'cd', message: 'cd はいどうするコマンドだよ。フォルダをつくるには、さっきつかったコマンドをもういちどつかおう。' },
533
+ ],
479
534
  },
480
535
  {
481
536
  id: 'obj-k1-03-03',
@@ -488,6 +543,11 @@ export const storyK1 = {
488
543
  { level: 2, text: '「touch」のあとにファイル名を書くと、ファイルがつくれるよ。' },
489
544
  { level: 3, text: '「touch 秘密基地/冒険ノート.txt」とにゅうりょくしてね。' },
490
545
  ],
546
+ feedbacks: [
547
+ { pattern: 'mkdir', message: 'mkdir はフォルダをつくるコマンドだよ。からっぽのファイルをつくるには、べつのコマンドをつかうよ。' },
548
+ { pattern: 'echo', message: 'echo はもじをひょうじするコマンドだよ。からっぽのファイルをつくるには、べつのコマンドをつかうよ。' },
549
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。からっぽのファイルをつくるには、べつのコマンドをつかうよ。' },
550
+ ],
491
551
  },
492
552
  ],
493
553
  },
@@ -496,6 +556,13 @@ export const storyK1 = {
496
556
  id: 'mission-k1-04',
497
557
  title: '宝物を集めよう',
498
558
  description: 'いろんな場所にある宝物を秘密基地にあつめよう!',
559
+ goal: 'cp でコピー、mv でいどうができるようになる',
560
+ review: {
561
+ question: 'ファイルをコピーするコマンドはどれかな?',
562
+ choices: ['mv', 'cp', 'rm', 'cat'],
563
+ correctIndex: 1,
564
+ explanation: 'cp はファイルをコピーするコマンドだよ。もとのファイルはそのままのこるよ。「copy」のりゃくだよ。',
565
+ },
499
566
  narrative: '冒険の世界にはたからものがいっぱい!魔法の森にある宝石をコピーして、古いお城にある伝説の剣を秘密基地にうつそう。',
500
567
  initialCwd: '/冒険の世界',
501
568
  initialFS: mission4FS,
@@ -518,6 +585,11 @@ export const storyK1 = {
518
585
  text: '「cp 魔法の森/光る宝石.txt 秘密基地/宝物庫/光る宝石.txt」とにゅうりょくしてね。',
519
586
  },
520
587
  ],
588
+ feedbacks: [
589
+ { pattern: 'mv', message: 'mv はファイルをいどうするコマンドだよ。コピーするには、もとのファイルをのこしたままつくるコマンドをつかうよ。' },
590
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。ファイルをコピーするには、べつのコマンドをつかうよ。' },
591
+ { pattern: 'rm', message: 'rm はファイルをけすコマンドだよ。コピーするには、べつのコマンドをつかうよ。' },
592
+ ],
521
593
  },
522
594
  {
523
595
  id: 'obj-k1-04-02',
@@ -536,6 +608,11 @@ export const storyK1 = {
536
608
  text: '「mv 古いお城/宝物庫/伝説の剣.txt 秘密基地/宝物庫/伝説の剣.txt」とにゅうりょくしてね。',
537
609
  },
538
610
  ],
611
+ feedbacks: [
612
+ { pattern: 'cp', message: 'cp はコピーするコマンドだよ。もとのファイルをなくして、べつの場所にうつすには、べつのコマンドをつかうよ。' },
613
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。ファイルをうつすには、べつのコマンドをつかうよ。' },
614
+ { pattern: 'rm', message: 'rm はファイルをけすコマンドだよ。うつすには、けすのではなくいどうするコマンドをつかうよ。' },
615
+ ],
539
616
  },
540
617
  ],
541
618
  },
@@ -544,6 +621,13 @@ export const storyK1 = {
544
621
  id: 'mission-k1-05',
545
622
  title: 'トラップを片付けよう',
546
623
  description: 'ひみつの洞窟にしかけられたトラップを見つけて、かたづけよう!',
624
+ goal: 'find でファイルをさがし、rm でけせるようになる',
625
+ review: {
626
+ question: 'ファイルをけすコマンドはどれかな?',
627
+ choices: ['cp', 'mv', 'rm', 'find'],
628
+ correctIndex: 2,
629
+ explanation: 'rm はファイルをけすコマンドだよ。「remove」のりゃくだよ。けしたファイルはもとにもどせないから、きをつけてつかおうね。',
630
+ },
547
631
  narrative: 'ひみつの洞窟にはトラップがしかけてある!まずはトラップがどこにあるか見つけて、ぜんぶかたづけよう。そうすれば安全にたからものがとれるよ。',
548
632
  initialCwd: '/冒険の世界/ひみつの洞窟',
549
633
  initialFS: mission5FS,
@@ -561,6 +645,11 @@ export const storyK1 = {
561
645
  text: '「find . -name トラップ*」とにゅうりょくしてね。',
562
646
  },
563
647
  ],
648
+ feedbacks: [
649
+ { pattern: 'ls', message: 'ls はいまいるフォルダの中身を見るコマンドだよ。おくのフォルダもふくめてさがすには、べつのコマンドをつかうよ。' },
650
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。ファイルをさがすには、べつのコマンドをつかうよ。' },
651
+ { pattern: 'grep', message: 'grep はファイルの中のことばをさがすコマンドだよ。ファイルの名前でさがすには、べつのコマンドをつかうよ。' },
652
+ ],
564
653
  },
565
654
  {
566
655
  id: 'obj-k1-05-02',
@@ -576,6 +665,10 @@ export const storyK1 = {
576
665
  { level: 2, text: '「rm」のあとにけしたいファイル名を書くよ。' },
577
666
  { level: 3, text: '「rm トラップ1.txt」とにゅうりょくしてね。' },
578
667
  ],
668
+ feedbacks: [
669
+ { pattern: 'mv', message: 'mv はファイルをいどうするコマンドだよ。ファイルをけすには、べつのコマンドをつかうよ。' },
670
+ { pattern: 'find', message: 'find はファイルをさがすコマンドだよ。もうみつけたから、つぎはけすコマンドをつかおう。' },
671
+ ],
579
672
  },
580
673
  {
581
674
  id: 'obj-k1-05-03',
@@ -591,6 +684,9 @@ export const storyK1 = {
591
684
  { level: 2, text: '「rm」コマンドをつかおう。' },
592
685
  { level: 3, text: '「rm トラップ2.txt」とにゅうりょくしてね。' },
593
686
  ],
687
+ feedbacks: [
688
+ { pattern: 'mv', message: 'mv はいどうするコマンドだよ。けすには、さっきとおなじコマンドをつかおう。' },
689
+ ],
594
690
  },
595
691
  {
596
692
  id: 'obj-k1-05-04',
@@ -606,6 +702,9 @@ export const storyK1 = {
606
702
  { level: 2, text: 'パスを指定すればべつのフォルダの中のファイルもけせるよ。' },
607
703
  { level: 3, text: '「rm 奥の部屋/トラップ3.txt」とにゅうりょくしてね。' },
608
704
  ],
705
+ feedbacks: [
706
+ { pattern: 'cd', message: 'cd でいどうしなくても、パスをしていすればべつのフォルダの中のファイルもけせるよ。' },
707
+ ],
609
708
  },
610
709
  ],
611
710
  },
@@ -614,6 +713,13 @@ export const storyK1 = {
614
713
  id: 'mission-k1-06',
615
714
  title: '暗号を解読しよう',
616
715
  description: '暗号文から手がかりをさがして、けっかを書きのこそう!',
716
+ goal: 'grep でことばをさがし、echo でファイルに書きこめるようになる',
717
+ review: {
718
+ question: 'ファイルの中からことばをさがすコマンドはどれかな?',
719
+ choices: ['find', 'cat', 'grep', 'echo'],
720
+ correctIndex: 2,
721
+ explanation: 'grep はファイルの中からことばをさがすコマンドだよ。find がファイルの「名前」をさがすのにたいして、grep はファイルの「中身」をさがすよ。',
722
+ },
617
723
  narrative: '洞窟のおくで暗号文を見つけた!この中に「魔法」というキーワードがかくれているらしい。さがしだして、わかったことを書きのこそう。',
618
724
  initialCwd: '/冒険の世界/ひみつの洞窟',
619
725
  initialFS: mission6FS,
@@ -628,6 +734,10 @@ export const storyK1 = {
628
734
  { level: 2, text: '「grep」のあとにさがしたいことばとファイル名を書くよ。' },
629
735
  { level: 3, text: '「grep 魔法 暗号文.txt」とにゅうりょくしてね。' },
630
736
  ],
737
+ feedbacks: [
738
+ { pattern: 'cat', message: 'cat はファイルのぜんぶを読むコマンドだよ。とくていのことばだけさがすには、べつのコマンドをつかうよ。' },
739
+ { pattern: 'find', message: 'find はファイルの名前をさがすコマンドだよ。ファイルの中身からことばをさがすには、べつのコマンドをつかうよ。' },
740
+ ],
631
741
  },
632
742
  {
633
743
  id: 'obj-k1-06-02',
@@ -646,6 +756,10 @@ export const storyK1 = {
646
756
  text: '「echo "魔法使いが宝の地図をくれた" > 解読結果.txt」とにゅうりょくしてね。',
647
757
  },
648
758
  ],
759
+ feedbacks: [
760
+ { pattern: 'touch', message: 'touch はからっぽのファイルをつくるコマンドだよ。中身もいっしょに書きこむには、べつのコマンドと「>」をつかうよ。' },
761
+ { pattern: 'cat', message: 'cat はファイルを読むコマンドだよ。ファイルに書きこむには、べつのコマンドと「>」をつかうよ。' },
762
+ ],
649
763
  },
650
764
  ],
651
765
  },
@@ -654,6 +768,13 @@ export const storyK1 = {
654
768
  id: 'mission-k1-07',
655
769
  title: '古い書物を読もう',
656
770
  description: 'ながい魔法の書の最初と最後をかくにんして、何ページあるかかぞえよう!',
771
+ goal: 'head と tail でファイルの一部を読み、wc で行数をかぞえられるようになる',
772
+ review: {
773
+ question: 'ファイルのさいしょの方だけ読むコマンドはどれかな?',
774
+ choices: ['cat', 'head', 'tail', 'wc'],
775
+ correctIndex: 1,
776
+ explanation: 'head はファイルのさいしょの方だけひょうじするコマンドだよ。さいごの方を見るには tail をつかうよ。',
777
+ },
657
778
  narrative: '古いお城の図書室で、ぶあつい魔法の書を見つけた!ぜんぶ読むのは大変だから、最初と最後だけ読んで、ページ数もかくにんしよう。',
658
779
  initialCwd: '/冒険の世界/古いお城/図書室',
659
780
  initialFS: mission7FS,
@@ -668,6 +789,10 @@ export const storyK1 = {
668
789
  { level: 2, text: '「head」コマンドをつかうと、ファイルのさいしょの方だけ見られるよ。' },
669
790
  { level: 3, text: '「head 魔法の書.txt」とにゅうりょくしてね。' },
670
791
  ],
792
+ feedbacks: [
793
+ { pattern: 'cat', message: 'cat はファイルのぜんぶをひょうじするよ。さいしょの方だけ見るには、べつのコマンドをつかうよ。' },
794
+ { pattern: 'tail', message: 'tail はファイルのさいごの方をひょうじするコマンドだよ。さいしょの方を見るには、べつのコマンドをつかうよ。' },
795
+ ],
671
796
  },
672
797
  {
673
798
  id: 'obj-k1-07-02',
@@ -678,6 +803,10 @@ export const storyK1 = {
678
803
  { level: 2, text: '「tail」コマンドをつかうと、ファイルのさいごの方だけ見られるよ。' },
679
804
  { level: 3, text: '「tail 魔法の書.txt」とにゅうりょくしてね。' },
680
805
  ],
806
+ feedbacks: [
807
+ { pattern: 'cat', message: 'cat はファイルのぜんぶをひょうじするよ。さいごの方だけ見るには、べつのコマンドをつかうよ。' },
808
+ { pattern: 'head', message: 'head はファイルのさいしょの方をひょうじするコマンドだよ。さいごの方を見るには、べつのコマンドをつかうよ。' },
809
+ ],
681
810
  },
682
811
  {
683
812
  id: 'obj-k1-07-03',
@@ -691,6 +820,11 @@ export const storyK1 = {
691
820
  { level: 2, text: '「wc」コマンドをつかうと、行の数がわかるよ。' },
692
821
  { level: 3, text: '「wc 魔法の書.txt」とにゅうりょくしてね。' },
693
822
  ],
823
+ feedbacks: [
824
+ { pattern: 'cat', message: 'cat はファイルの中身をひょうじするよ。行の数をかぞえるには、べつのコマンドをつかうよ。' },
825
+ { pattern: 'head', message: 'head はファイルのさいしょの方をひょうじするよ。行数をかぞえるには、べつのコマンドをつかうよ。' },
826
+ { pattern: 'tail', message: 'tail はファイルのさいごの方をひょうじするよ。行数をかぞえるには、べつのコマンドをつかうよ。' },
827
+ ],
694
828
  },
695
829
  ],
696
830
  },
@@ -699,6 +833,13 @@ export const storyK1 = {
699
833
  id: 'mission-k1-08',
700
834
  title: '仲間リストを整理しよう',
701
835
  description: '冒険者の名簿をきれいに整理しよう!',
836
+ goal: 'sort、uniq、cut をつかってデータを整理できるようになる',
837
+ review: {
838
+ question: 'おなじ行をまとめてくれるコマンドはどれかな?',
839
+ choices: ['sort', 'uniq', 'cut', 'grep'],
840
+ correctIndex: 1,
841
+ explanation: 'uniq はとなりあうおなじ行をまとめるコマンドだよ。sort でならべてから uniq をつかうと、ぜんぶのおなじ行をまとめられるよ。',
842
+ },
702
843
  narrative: '古いお城で冒険者の名簿を見つけた。でも同じ名前が何回も書いてある。きれいに整理して、どんな職業の仲間がいるか調べよう。',
703
844
  initialCwd: '/冒険の世界/古いお城',
704
845
  initialFS: mission8FS,
@@ -713,6 +854,10 @@ export const storyK1 = {
713
854
  { level: 2, text: '「sort」コマンドで行をじゅんばんにできるよ。' },
714
855
  { level: 3, text: '「sort 冒険者名簿.csv」とにゅうりょくしてね。' },
715
856
  ],
857
+ feedbacks: [
858
+ { pattern: 'cat', message: 'cat はファイルをそのままひょうじするよ。じゅんばんにならべるには、べつのコマンドをつかうよ。' },
859
+ { pattern: 'uniq', message: 'uniq はおなじ行をまとめるコマンドだよ。まずはじゅんばんにならべるコマンドをつかおう。' },
860
+ ],
716
861
  },
717
862
  {
718
863
  id: 'obj-k1-08-02',
@@ -723,6 +868,10 @@ export const storyK1 = {
723
868
  { level: 2, text: '「sort」したあとに「|」で「uniq」につなげると、おなじ行をまとめられるよ。' },
724
869
  { level: 3, text: '「sort 冒険者名簿.csv | uniq」とにゅうりょくしてね。' },
725
870
  ],
871
+ feedbacks: [
872
+ { pattern: 'grep', message: 'grep はことばをさがすコマンドだよ。おなじ行をまとめるには、べつのコマンドをつかうよ。' },
873
+ { pattern: 'cat', message: 'cat はファイルをそのままひょうじするよ。おなじ行をまとめるには、sort と パイプ「|」をつかうよ。' },
874
+ ],
726
875
  },
727
876
  {
728
877
  id: 'obj-k1-08-03',
@@ -736,6 +885,10 @@ export const storyK1 = {
736
885
  text: '「cut -d, -f2 冒険者名簿.csv」とにゅうりょくしてね。',
737
886
  },
738
887
  ],
888
+ feedbacks: [
889
+ { pattern: 'grep', message: 'grep はことばをさがすコマンドだよ。とくていの部分だけきりだすには、べつのコマンドをつかうよ。' },
890
+ { pattern: 'cat', message: 'cat はファイルのぜんぶをひょうじするよ。とくていの部分だけきりだすには、べつのコマンドをつかうよ。' },
891
+ ],
739
892
  },
740
893
  ],
741
894
  },
@@ -744,6 +897,13 @@ export const storyK1 = {
744
897
  id: 'mission-k1-09',
745
898
  title: '封印を解こう',
746
899
  description: 'ふういんされた呪文をとけるようにして、魔法をつかおう!',
900
+ goal: 'chmod でけんげんをかえ、パイプで grep をつなげられるようになる',
901
+ review: {
902
+ question: 'ファイルのけんげん(つかえるかどうか)をかえるコマンドはどれかな?',
903
+ choices: ['chown', 'chmod', 'mv', 'cp'],
904
+ correctIndex: 1,
905
+ explanation: 'chmod はファイルのけんげん(パーミッション)をかえるコマンドだよ。「change mode」のりゃくだよ。',
906
+ },
747
907
  narrative: '古いお城のおくで、ふういんされた呪文を見つけた!ふういんを解いて実行できるようにしよう。そして魔法の石から、つかいたい魔法をえらびだそう。',
748
908
  initialCwd: '/冒険の世界/古いお城',
749
909
  initialFS: mission9FS,
@@ -758,6 +918,10 @@ export const storyK1 = {
758
918
  { level: 2, text: '「chmod」コマンドで「+x」をつけると、実行できるようになるよ。' },
759
919
  { level: 3, text: '「chmod +x 封印の呪文.sh」とにゅうりょくしてね。' },
760
920
  ],
921
+ feedbacks: [
922
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。ふういんを解く(けんげんをかえる)には、べつのコマンドをつかうよ。' },
923
+ { pattern: 'rm', message: 'rm はファイルをけすコマンドだよ。ふういんを解くには、けんげんをかえるコマンドをつかおう。' },
924
+ ],
761
925
  },
762
926
  {
763
927
  id: 'obj-k1-09-02',
@@ -768,6 +932,9 @@ export const storyK1 = {
768
932
  { level: 2, text: '「cat」でファイルを読んで、「|」で「grep」につなげると、ほしい行だけとりだせるよ。' },
769
933
  { level: 3, text: '「cat 魔法の石.txt | grep 光」とにゅうりょくしてね。' },
770
934
  ],
935
+ feedbacks: [
936
+ { pattern: 'find', message: 'find はファイルの名前をさがすコマンドだよ。ファイルの中身からさがすには、grep をつかうよ。パイプ「|」でつなげてみよう。' },
937
+ ],
771
938
  },
772
939
  ],
773
940
  },
@@ -776,6 +943,13 @@ export const storyK1 = {
776
943
  id: 'mission-k1-10',
777
944
  title: '冒険の記録をつけよう',
778
945
  description: 'gitをつかって、冒険のきろくをかくにんしよう!',
946
+ goal: 'git status と git log で変更のきろくをかくにんできるようになる',
947
+ review: {
948
+ question: 'いままでのきろく(ログ)を見るgitコマンドはどれかな?',
949
+ choices: ['git status', 'git log', 'git add', 'git diff'],
950
+ correctIndex: 1,
951
+ explanation: 'git log はいままでのへんこうのきろく(コミット)をひょうじするコマンドだよ。git status はいまのじょうたいをたしかめるコマンドだよ。',
952
+ },
779
953
  narrative: '秘密基地にもどってきた。冒険のきろくがgitでのこしてある。いままでの冒険をふりかえってみよう!',
780
954
  initialCwd: '/冒険の世界/秘密基地',
781
955
  initialFS: mission10FS,
@@ -793,6 +967,10 @@ export const storyK1 = {
793
967
  { level: 2, text: '「git status」でいまのじょうたいがわかるよ。' },
794
968
  { level: 3, text: '「git status」とにゅうりょくしてね。' },
795
969
  ],
970
+ feedbacks: [
971
+ { pattern: 'ls', message: 'ls はフォルダの中身を見るコマンドだよ。gitのじょうたいを見るには、git のコマンドをつかうよ。' },
972
+ { pattern: 'cat', message: 'cat はファイルを読むコマンドだよ。gitのじょうたいをかくにんするには、git のコマンドをつかうよ。' },
973
+ ],
796
974
  },
797
975
  {
798
976
  id: 'obj-k1-10-02',
@@ -806,6 +984,9 @@ export const storyK1 = {
806
984
  { level: 2, text: '「git log」でいままでのきろくが見られるよ。' },
807
985
  { level: 3, text: '「git log」とにゅうりょくしてね。' },
808
986
  ],
987
+ feedbacks: [
988
+ { pattern: 'cat', message: 'cat はファイルの中身を読むコマンドだよ。gitのきろくを見るには、git のコマンドをつかうよ。' },
989
+ ],
809
990
  },
810
991
  ],
811
992
  },
@@ -28,6 +28,16 @@ export interface Objective {
28
28
  description: string;
29
29
  checks: ObjectiveCheck[];
30
30
  hints: Hint[];
31
+ feedbacks?: Array<{
32
+ pattern: string;
33
+ message: string;
34
+ }>;
35
+ }
36
+ export interface ReviewQuestion {
37
+ question: string;
38
+ choices: string[];
39
+ correctIndex: number;
40
+ explanation: string;
31
41
  }
32
42
  export interface FSNode {
33
43
  type: 'file' | 'directory';
@@ -44,6 +54,8 @@ export interface Mission {
44
54
  initialFS: FSNode;
45
55
  initialCwd: string;
46
56
  newCommands?: string[];
57
+ goal?: string;
58
+ review?: ReviewQuestion;
47
59
  }
48
60
  export interface Story {
49
61
  id: string;
@@ -59,6 +71,7 @@ export interface StoryProgress {
59
71
  completedMissions: string[];
60
72
  currentMissionIndex: number;
61
73
  hintsUsed: Record<string, number>;
74
+ commandsPerMission?: Record<string, number>;
62
75
  }
63
76
  export interface Achievement {
64
77
  id: string;
@@ -89,6 +102,7 @@ export type Screen = {
89
102
  type: 'missionComplete';
90
103
  storyId: string;
91
104
  missionIndex: number;
105
+ commandCount?: number;
92
106
  } | {
93
107
  type: 'progress';
94
108
  } | {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Levenshtein distance-based command suggestion.
3
+ * Returns a suggestion string if a close match is found, otherwise null.
4
+ */
5
+ export declare function suggestCommand(input: string): string | null;
6
+ /**
7
+ * Check mission-specific feedback patterns.
8
+ * Returns a feedback message if the input matches a pattern, otherwise null.
9
+ */
10
+ export declare function checkMissionFeedback(input: string, feedbacks: Array<{
11
+ pattern: string;
12
+ message: string;
13
+ }>): string | null;
14
+ //# sourceMappingURL=CommandFeedback.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { commandRegistry } from './commands/index.js';
2
+ /**
3
+ * Levenshtein distance-based command suggestion.
4
+ * Returns a suggestion string if a close match is found, otherwise null.
5
+ */
6
+ export function suggestCommand(input) {
7
+ const cmd = input.split(/\s+/)[0];
8
+ if (!cmd || commandRegistry[cmd])
9
+ return null;
10
+ const commands = Object.keys(commandRegistry);
11
+ let bestMatch = '';
12
+ let bestDistance = Infinity;
13
+ for (const known of commands) {
14
+ const dist = levenshtein(cmd, known);
15
+ if (dist < bestDistance) {
16
+ bestDistance = dist;
17
+ bestMatch = known;
18
+ }
19
+ }
20
+ // Only suggest if distance is 2 or less (reasonable typo range)
21
+ if (bestDistance <= 2 && bestMatch) {
22
+ return bestMatch;
23
+ }
24
+ return null;
25
+ }
26
+ /**
27
+ * Check mission-specific feedback patterns.
28
+ * Returns a feedback message if the input matches a pattern, otherwise null.
29
+ */
30
+ export function checkMissionFeedback(input, feedbacks) {
31
+ const cmd = input.split(/\s+/)[0];
32
+ for (const fb of feedbacks) {
33
+ if (cmd === fb.pattern || input.startsWith(fb.pattern)) {
34
+ return fb.message;
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+ function levenshtein(a, b) {
40
+ const m = a.length, n = b.length;
41
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
42
+ for (let i = 0; i <= m; i++)
43
+ dp[i][0] = i;
44
+ for (let j = 0; j <= n; j++)
45
+ dp[0][j] = j;
46
+ for (let i = 1; i <= m; i++) {
47
+ for (let j = 1; j <= n; j++) {
48
+ dp[i][j] = a[i - 1] === b[j - 1]
49
+ ? dp[i - 1][j - 1]
50
+ : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
51
+ }
52
+ }
53
+ return dp[m][n];
54
+ }
55
+ //# sourceMappingURL=CommandFeedback.js.map